第一章:Filter
1.1 Filter简介
1.1.1 什么是Filter?
- Filter的中文意思是过滤器。顾名思义,过滤器可以在浏览器和目标资源之间起到一个过滤的作用。如:水净化器,可以看成是生活中的一个过滤器,可以将污水中的杂质过滤掉,从而使得污水变为净水。
- 对于WEB应用来说,过滤器是一个驻留在服务器中的WEB组件,可以截取客户端和WEB资源(包括Servlet、Thymeleaf、JSP、HTML、图片、视频等)之间的请求和响应信息。
- 当服务器收到特定的请求后,会先将请求交给过滤器,程序员可以在过滤器中对请求信息进行读取和修改等操作,然后将请求信息再转发给目标资源,目标资源作出响应后,服务器会再次将响应转交给过滤器,在过滤器中同样可以对响应信息做一些操作,然后再将响应发送给浏览器。
- 换言之,过滤器可以在WEB资源收到请求之前,浏览器接收到响应之前,对请求和响应信息做一些相应的操作。
- 在一个WEB应用中可以部署多个过滤器,多个过滤器组成了一个过滤器链,请求和响应必然会经过多个过滤器后才能到达目标。
- 过滤器不是必然将请求传递给下一个过滤器或WEB资源,也可以自己来处理请求、发出响应信息。
- 当配置多个Filter的时候,会有一个执行顺序的问题,实际执行顺序是按照在
web.xml
中的servlet-mapping
的顺序决定的,如果顺序靠前就越先被调用;但是如果采用的是注解方式,是没有任何顺序而言的
。
1.1.2 Filter小结
- Filter是一个接口。
- Filter是Java WEB的三大组件之一(Java Web的三大组件:Servlet、Filter、Listener)。
- Filter是服务器专门用来过滤请求、拦截响应的。
1.1.3 Filter的应用场景
- ① 检查用户访问权限。
- ② 设置请求响应编码、解决乱码问题。
- ③ 过滤非法字符。
- ④ ……
1.2 Filter的主要API
1.2.1 Filter接口
- 编写Filter都需要实现Filter接口,Filter接口的主要方法如下:
- 初始化Filter:
default public void init(FilterConfig filterConfig) throws ServletException {}
- 过滤请求和响应:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;
- 在Filter对象被销毁前做一些收尾工作(如:释放资源等):
default public void destroy() {}
1.2.2 FilterConfig接口
- FilterConfig对象在服务器调用init方法的时候传递进来。
- 获取Filter的名称:
public String getFilterName();
- 获取ServletContext对象:
public ServletContext getServletContext();
- 根据指定的初始化参数名获取初始化参数值:
public String getInitParameter(String name);
- 获取所有的初始化参数名:
public Enumeration<String> getInitParameterNames();
1.2.3 FilterChain接口
- FilterChain对象是在doFilter()方法被调用的时候作为参数传递进来使用的。
- 调用FIlter链上的下一个过滤器,如果当前过滤器是最后一个过滤器则将请求发送到目标资源。
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
1.3 Filter入门
- ① 编写一个类实现Filter接口,重写Filter接口中的所有方法。
- ② 在web.xml配置Filter或使用@WebFilter注解配置Filter。
- ③ 在doFilter()方法中写一些逻辑,并对请求和响应进行放行。
④ 启动服务器进行测试。
示例:请求编码过滤器
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app/test1" method="post">
用户名:<input name="username" type="text"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
- Test1Servlet.java
package com.example.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Test1Servlet", value = "/test1")
public class Test1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test1Servlet username = " + username);
}
}
- Test2Servlet.java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Test2Servlet", value = "/test2")
public class Test2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test2Servlet username = " + username);
}
}
- EncodingFilter.java
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* DispatcherType.REQUEST:用户直接访问资源时,会调用Filter,默认。
* DispatcherType.INCLUDE:通过动态包含获取时,会调用Filter
* DispatcherType.ERROR:当通过异常处理访问页面时,会调用Filter
* DispatcherType.FORWARD:通过转发访问时,会调用Filter
*/
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*", dispatcherTypes = DispatcherType.REQUEST)
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
// 放行
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
1.4 Filter的生命周期
1.4.1 回顾Servlet的生命周期
- ① 先执行构造方法。
- ② 执行init方法做初始化操作。
- ③ 执行service方法。
- ④ 销毁的时候调用destory方法。
1.4.2 Filter的生命周期
- ① 先执行Filter的构造方法。
- ② 然后执行Filter的init()方法,对象创建后,马上就被调用,对Filter做一些初始化操作。
- ③ 执行Filter的doFilter()方法,每次访问目标资源,只要匹配过滤的地址,就会调用。
- ④ 执行Filter的destroy()方法,服务器停止时调用,用来释放资源。
1.5 过滤器匹配规则
1.5.1 过滤器匹配的目的
- 过滤器匹配的目的是指定当前过滤器要拦截哪些资源。
1.5.2 过滤器匹配规则之精确匹配
- 精确匹配:指定被拦截资源的完成路径。
// 只拦截/test1所在的资源
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/test1")
- 示例:
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app/test2" method="post">
用户名:<input name="username" type="text"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
- Test1Servlet.java
package com.example.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Test1Servlet", value = "/test1")
public class Test1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test1Servlet username = " + username);
}
}
- Test2Servlet.java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Test2Servlet", value = "/test2")
public class Test2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test2Servlet username = " + username);
}
}
- EncodingFilter.java
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/test1")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
// 放行
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
1.5.3 过滤器匹配规则之模糊匹配
- 相比较精确匹配,使用模糊匹配可以让我们创建一个Filter就能够覆盖很多目标资源,不必专门为每一个目标资源都创建Filter,提高开发效率。
// 拦截一切资源,包括静态资源和动态资源
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
- 示例:
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app/test2" method="post">
用户名:<input name="username" type="text"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
- Test1Servlet.java
package com.example.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Test1Servlet", value = "/test1")
public class Test1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test1Servlet username = " + username);
}
}
- Test2Servlet.java
package com.example.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "Test2Servlet", value = "/test2")
public class Test2Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test2Servlet username = " + username);
}
}
- EncodingFilter.java
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
// 放行
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
1.5.3 过滤器匹配规则之扩展名匹配
- 扩展名匹配:拦截指定扩展名的资源。
@WebFilter(filterName = "EncodingFilter", urlPatterns = ".html")
- 示例:
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "EncodingFilter", urlPatterns = ".html")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
// 放行
chain.doFilter(req, resp);
}
@Override
public void destroy() {
}
}
1.6 过滤器链
- FilterChain是整个Filter过滤器的调用者。Filter与Filter之间的传递,或者Filter与请求资源之间的传递都靠
FilterChain.doFilter
方法。 - 一般Filter.doFilter中的代码分为三段:
- 第一段是FilterChain.doFilter之前的代码。一般用来做请求的拦截,检查用户访问的权限,访问日记的记录。参数编码的设置等等操作。
- 第二段是FilterChain.doFilter方法。此方法可以将代码的执行传递到下一个Filter中。或者是传递到用户最终访问的资源中。
- 第三段是FilterChain.doFilter之后的代码。主要用过做一些日志操作。我们很少会在第三段中做太多复杂的操作。
- 在每一个Filter类的doFilter方法中,一定要调用chain.doFilter方法,除非你想要阻止用户继续往下面访问。否则一定要调用FilterChain的doFilter方法。
- 示例:
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/app/test1" method="post">
用户名:<input name="username" type="text"> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
- Test1Servlet.java
package com.example.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "Test1Servlet", value = "/test1")
public class Test1Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println("Test1Servlet username = " + username);
}
}
- Test1Filter.java
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "Test1Filter", urlPatterns = "/*")
public class Test1Filter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
System.out.println("资源访问前---Test1Filter -- 开始执行");
// 放行
chain.doFilter(req, resp);
System.out.println("资源访问后---Test1Filter -- 执行结束");
}
@Override
public void destroy() {
}
}
- Test2Filter.java
package com.example.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "Test2Filter", urlPatterns = "/*")
public class Test2Filter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 解决请求参数的乱码
req.setCharacterEncoding("UTF-8");
System.out.println("资源访问前---Test2Filter -- 开始执行");
// 放行
chain.doFilter(req, resp);
System.out.println("资源访问后---Test2Filter -- 执行结束");
}
@Override
public void destroy() {
}
}
第二章:Listener
2.1 观察者模式简介
- 观察者模式是23种设计模式之一,它是指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖它的对象都得到通知并且被自动更新,这种模式有时也称为发布—订阅模式。
观察者
:监控被观察者
的行为,一旦发现被观察者
触发了事件,就会调用事先准备好的方法执行操作。被观察者
:被观察者
一旦触发了被监控的事件,就会被观察者
发现。
2.2 监听器的简介
- 监听器:专门用来对其他对象身上发生的事件或状态改变进行监听和作出相应处理的对象,当被监视的对象发生情况的时候,立即采取相应的行动。
- Servlet监视器:Servlet规范中定义的一种特殊的类,它用于监听WEB应用程序中的ServletContext、HttpSession、HttpServletRequest等域对象的创建和销毁以及监听这些域对象中的属性发生修改的事件。
2.3 Servlet监听器的分类
2.3.1 ServletContextListener
- 作用:监听ServletContext对象的创建与销毁。 | 方法名 | 作用 | | —- | —- | | contextInitialized(ServletContextEvent sce) | ServletContext创建时调用 | | contextDestroyed(ServletContextEvent sce) | ServletContext销毁时调用 |
- ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。
2.3.2 HttpSessionListener
- 作用:监听HttpSession对象的创建与销毁。 | 方法名 | 作用 | | —- | —- | | sessionCreated(HttpSessionEvent hse) | HttpSession对象创建时调用 | | sessionDestroyed(HttpSessionEvent hse) | HttpSession对象销毁时调用 |
- HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。
2.3.3 ServletRequestListener
- 作用:监听ServletRequest对象的创建与销毁。 | 方法名 | 作用 | | —- | —- | | requestInitialized(ServletRequestEvent sre) | ServletRequest对象创建时调用 | | requestDestroyed(ServletRequestEvent sre) | ServletRequest对象销毁时调用 |
- ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。
2.3.4 ServletContextAttributeListener
- 作用:监听ServletContext中属性的创建、修改和销毁。 | 方法名 | 作用 | | —- | —- | | attributeAdded(ServletContextAttributeEvent scab) | 向ServletContext中添加属性时调用 | | attributeRemoved(ServletContextAttributeEvent scab) | 从ServletContext中移除属性时调用 | | attributeReplaced(ServletContextAttributeEvent scab) | 当ServletContext中的属性被修改时调用 |
- ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下: | 方法名 | 作用 | | —- | —- | | getName() | 获取修改或添加的属性名 | | getValue() | 获取被修改或添加的属性值 | | getServletContext() | 获取ServletContext对象 |
2.3.5 HttpSessionAttributeListener
- 作用:监听HttpSession中属性的创建、修改和销毁。 | 方法名 | 作用 | | —- | —- | | attributeAdded(HttpSessionBindingEvent se) | 向HttpSession中添加属性时调用 | | attributeRemoved(HttpSessionBindingEvent se) | 从HttpSession中移除属性时调用 | | attributeReplaced(HttpSessionBindingEvent se) | 当HttpSession中的属性被修改时调用 |
- HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下: | 方法名 | 作用 | | —- | —- | | getName() | 获取修改或添加的属性名 | | getValue() | 获取被修改或添加的属性值 | | getSession() | 获取触发事件的HttpSession对象 |
2.3.6 ServletRequestAttributeListener
- 作用:监听ServletRequest中属性的创建、修改和销毁。 | 方法名 | 作用 | | —- | —- | | attributeAdded(ServletRequestAttributeEvent srae) | 向ServletRequest中添加属性时调用 | | attributeRemoved(ServletRequestAttributeEvent srae) | 从ServletRequest中移除属性时调用 | | attributeReplaced(ServletRequestAttributeEvent srae) | 当ServletRequest中的属性被修改时调用 |
- ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下: | 方法名 | 作用 | | —- | —- | | getName() | 获取修改或添加的属性名 | | getValue() | 获取被修改或添加的属性值 | | getServletRequest () | 获取触发事件的ServletRequest对象 |
2.3.7 HttpSessionBindingListener
- 作用:监听某个对象在Session域中的创建与移除。 | 方法名 | 作用 | | —- | —- | | valueBound(HttpSessionBindingEvent event) | 该类的实例被放到Session域中时调用 | | valueUnbound(HttpSessionBindingEvent event) | 该类的实例从Session中移除时调用 |
- HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下: | 方法名 | 作用 | | —- | —- | | getName() | 获取当前事件涉及的属性名 | | getValue() | 获取当前事件涉及的属性值 | | getSession() | 获取触发事件的HttpSession对象 |
2.3.8 HttpSessionActivationListener
- 作用:监听某个对象在Session中的序列化与反序列化。 | 方法名 | 作用 | | —- | —- | | sessionWillPassivate(HttpSessionEvent se) | 该类实例和Session一起钝化到硬盘时调用 | | sessionDidActivate(HttpSessionEvent se) | 该类实例和Session一起活化到内存时调用 |
- HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象。
2.4 ServletContextListener的使用
- ServletContextListener是监听ServletContext对象的创建和销毁的,因为ServletContext对象是在服务器启动的时候创建,在服务器关闭的时候销毁,所以ServletContextListener也可以监听服务器的启动和关闭。
在以后学习SpringMVC的时候,会用到ContextLoaderListener,这个监听器就是实现了ServletContextListener接口,表示对ServletContextListener对象本身的生命周期进行监控。
示例:
package com.example.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* @author 许大仙
* @version 1.0
* @since 2021-10-28 19:51
*/
@WebListener
public class ContextLoadListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("服务器启动了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("服务器关闭了");
}
}