在开始阅读这篇文章之前你应该对servlet有了基本的认识,如果没有可参考我的另外一篇文章——servlet及其生命周期。话不多说,今天开始介绍Java三大器:Listener,Filter,Interceptor。文章结构如图所示:
1.监听器(待补充)
2.过滤器
2.1定义功能
定义:Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,可以用来转换HTTP请求,响应和头信息。它不能产生一个请求或者响应,只是修改对某一资源的请求或响应;
功能:通过Filter技术,对web服务器管理的所有web资源:例如Jsp,Servlet, 静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等。
2.2工作过程
2.2.1工作原理
如上图所示
Filter接口中有一个doFilter()方法,当实现好Filter接口,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下Filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行。
是否调用目标资源(通过是否调用FilterChain对象的doFilter()方法实现)。
调用目标资源之后,让一段代码执行。
web服务器在调用doFilter()方法时,会传递一个FilterChain对象进来,FilterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter()方法,如果调用FilterChain对象的doFilter()方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。如果不调用FilterChain对象的doFilter()方法,则web资源不会被访问。
2.2.2生命周期
Filter的生命周期和Servlet一样,Filter的创建和销毁也是由web服务器负责。
1.在应用启动的时候就进行加载Filter类。
2.容器创建好Filter对象实例后,调用init()方法,。接着被Web容器保存进应用级的集合容器中去了等待着,用户访问资源。
3.当用户访问的资源正好被Filter的url-pattern拦截时,容器会取出Filter类调用doFilter()方法,下次或多次访问被拦截的资源时,Web容器会直接取出指定Filter对象实例调用doFilter方法。
4.当应用服务被停止或重新装载了,则会执行Filter的destroy方法,Filter对象销毁。
注意:init方法与destroy方法只会直接一次。
2.3代码实现
Filter开发分为2步:
1.编写java类实现Filter接口,并重写其doFilter()方法。
public class FilterTest implements Filter {
//对filter进行一个初始化
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter is initing please wait");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8");
//调用servlet方法前(访问web资源前执行的代码,相当于图中的code1)
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("filter before service() do");
filterChain.doFilter(servletRequest,servletResponse);
//调用servlet方法后(获得web资源后执行的代码,相当于图中的code2)
System.out.println("filter after service() do");
}
//调用destroy方法,filter被摧毁,生命周期结束
@Override
public void destroy() {
System.out.println("filter is destroy");
}
}
2.在web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。注册和映射的配置是必不可少的。
<!-- 过滤器配置一-->
<filter>
<filter-name>FilterTest</filter-name>
<!-- 要填写全类名-->
<filter-class>com.zeron.anoservlet.FilterTest</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterTest</filter-name>
<!-- /*表示拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
3.在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。通过filterConfig对象的方法,就可获得:
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext():返回Servlet上下文对象的引用。
2.4特性总结
应用场景:获取用户身份,参数校验,设置字符编码,过滤敏感词汇,权限访问控制等等
总结:所谓过滤器就是对请求进行过滤,作用在servlet之前,它是系统级别的过滤,在实现上基于函数回调,依赖于servlet容器。
3.拦截器
3.1定义功能
定义:Java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。
功能:可以进行权限验证,审计日志等。
3.2工作过程
如图所示:
①程序先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理其中的方法,否则将不再向下执行。
②在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应。
③处理完所有业务后,才会执行afterCompletion()方法。
生命周期:加载配置文件后初始化拦截器,当有对Action的请求的时候,调用interceptor方法,最后也是根据服务器停止进行销毁;
对于多个拦截器的执行顺序(以2个为例)
1.请求指定Controller的url
2.拦截器1的preHandle():处理请求之前的业务,return true时
拦截器2的preHandle():处理请求之前的业务,return true时
3.执行Controller的url
4.拦截器2postHandle():处理响应之前业务
拦截器1postHandle():处理响应之前业务
5.视图渲染
6.拦截器2的afterCompletion():处理外所有业务之后执行【释放资源等】
拦截器1的afterCompletion():处理外所有业务之后执行【释放资源等】
3.3代码实现(待补充)
3.4特性总结
Java里的拦截器提供的是非系统级别的拦截,就覆盖面来说,不如过滤器强大,但是更有针对性。
Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现的动态代理。它依赖于具体的接口,在运行期间动态生成字节码。
拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或
者之后加入某些操作。
4.对比总结:
4.1过滤器和拦截器的执行顺序
4.2区别对比(简单理解过滤器为取你所想取,拦截器为拒你所想拒)
1.拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2.拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3.拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4.拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5.在action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。
4.3其他补充:Tomcat中的filter分析
Tomcat中的Filter 是采用责任链设计模式。
首先通过StandardWrapperValve.invoke()方法创建一个过滤器链,并把待执行的 servlet 对象存放到过滤器链中。
如果servlet对象和过滤器链都不为空,则开始调用filterChain的doFilter()方法。该方法有三个参数request对象,response对象,filter chain对象。如图
pos:为过滤器链中当前执行的过滤器下标。 n:过滤器链中的过滤器个数。
每执行一个过滤器则把过滤器链中的post+1(下标),直到所有的过滤器的doFilter方法都调用成功。
这行代码是整个责任链模式的精妙之处,进入doFilter()方法后,,首先会对request请求进行处理, 然后调用了过滤器链的doFilter()方法.,让每个过滤器可以调用过滤器链本身执行下一个过滤器。
为什么要调用过滤器链本身?因为当调用过滤器本身后, 程序将跳转回到过滤器链的doFilter方法执行, 这时pos为1, 也就是拿到第二个过滤器, 然后继续处理。
正是由于这个跳转, 使得过滤器中对response的处理暂时无法执行, 它必须等待上面的对过滤器链的方法返回才能被执行。执行流程如下图:
5.参考资料
https://blog.csdn.net/reggergdsg/article/details/52962774
https://blog.csdn.net/dianruanbin6930/article/details/102021043
https://www.cnblogs.com/hellovoyager1/p/9152292.html
https://blog.csdn.net/taifei/article/details/88572669
https://blog.csdn.net/m0_46425463/article/details/108011336