大家好,我是来自郑州分院的第10期java学员,今天我要讲拦截器、过滤器、监听器各有什么作用?例行公事,先来背景介绍
背景介绍
过滤器Filter,是Servlet2.3新增加的功能。主要对HttpServletRequest进行预处理,也对HttpServletResponse进行后处理,是典型的处理链。可以实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等。
监听器用于监听web应用中某些对象、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。
拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作。目前,我们需要掌握的主要是Spring的拦截器,Struts2的拦截器不用深究,知道即可。
知识剖析
所有的Servlet过滤器接口必须实现java.servlet.Filter接口,这个接口含有三个过滤器类必须实现的方法:
1.init(FilterConfig)
Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后,将调用这个方法。这个方法可以读取web.xml中的Servlet过滤器的配置初始化参数
2.doFilter(ServletRequest,ServletResponse,FilterChain)
这个方法完成实际的过滤操作,当客户请求访问于过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain参数用于访问后续过滤器
3.destroy()
Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源
Filter执行过程
一个request请求进来了,先把自己交给filterChain;
filterChain启动过滤器链,从头开始,把request交给第一个filter,并把自己传给filter;
filter在doFilter里做完自己的过滤逻辑,再调用filterChain的doFilter,以启动下一个过滤器;
filterChain游标移动,启动下一个过滤器,如此循环下去......
过滤器游标走到链的末尾,filterChain执行收尾工作;
现在来说说Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动
而启动,只初始化一次,随web应用的停止而销毁。
按监听的对象划分,可以分为
ServletContext对象监听器
HttpSession对象监听器
ServletRequest对象监听器
按监听的事件划分
对象自身的创建和销毁的监听器
对象中属性的创建和消除的监听器
session中的某个对象的状态变化的监听器
Session数据的钝化与活化:
由于session中保存大量访问网站相关的重要信息,因此过多的session数据就会服务器性能的下降,占用过多的内存。因此类似数据库对象的持久化,web容器也会把不常使用的session数据持久化到本地文件或者数据中。这些都是有web容器自己完成,不需要用户设定。
不用的session数据序列化到本地文件中的过程,就是钝化;
当再次访问需要到该session的内容时,就会读取本地文件,再次放入内存中,这个过程就是活化。
类似的,只要实现HttpSeesionActivationListener接口就是实现钝化与活化事件的监听:
1. Servlet上下文进行监听(Application级):
用于监听 ServletContext 对象的创建和删除以及属性的添加、删除和修改等操作,该监听器需要用到如下两个接口类:
(1) ServletContextAttributeListener:监听对 ServletContext 属性的操作,比如增加、删除、修改
attributeAdded(ServletContextAttributeEvent e) 添加属性时调用
attributeReplaced(ServletContextAttributeEvent e) 修改属性时调用
attributeRemoved(ServletContextAttributeEvent e) 删除属性时调用
(2) ServletContextListener:监听对 ServletContext 对象的创建和删除
contextInitialized(ServletContextEvent sce) 初始化时调用
contextDestroyed(ServletContextEvent sce) 销毁时调用,即当服务器重新加载时调用
2. 监听HTTP会话(Session级):
用于监听 HTTP 会话活动情况和 HTTP 会话中的属性设置情况,也可以监听 HTTP 会话的 active 和 passivate 情况等,该监听器需要用到如下多个接口类:
(1) HttpSessionListener:监听 HttpSession 的操作
sessionCreate(HttpSessionEvent se) 初始化时调用;
sessionDestroyed(httpSessionEvent se) 销毁时调用,即当用户注销时调用
(2) HttpSessionActivationListener:用于监听 HTTP 会话的 active 和 passivate 情况
Session数据的钝化与活化:
由于session中保存大量访问网站相关的重要信息,因此过多的session数据就会服务器性能的下降,占用过多的内存。因此类似数据库对象的持久化,web容器也会把不常使用的session数据持久化到本地文件或者数据中。这些都是有web容器自己完成,不需要用户设定。不用的session数据序列化到本地文件中的过程,就是钝化;当再次访问需要到该session的内容时,就会读取本地文件,再次放入内存中,这个过程就是活化。类似的,只要实现HttpSeesionActivationListener接口就是实现钝化与活化事件的监听:
(3) HttpSessionAttributeListener:监听 HttpSession 中的属性操作
attributeAdded(HttpSessionBindingEvent se) 添加属性时调用
attributeRemoved(HttpSessionBindingEvent se) 删除属性时调用
attributeReplaced(HttpSessionBindingEvent se) 修改属性时调用
3. 对客户端请求进行监听(Requst级):
用于对客户端的请求进行监听是在 Servlet2.4 规范中新添加的一项新技术,使用的接口如下:
(1) ServletRequestListener 接口类
requestDestroyed(ServletRequestEvent e) 对销毁客户端进行监听,即当执行 request.removeAttribute("xxx") 时调用
requestInitialized(ServletRequestEvent e) 对实现客户端的请求进行监听
(2) ServletRequestAttributeListener 接口类
attributeAdded(ServletRequestAttributeEvent e) 对属性添加进行监听
attributeRemoved(ServletRequestAttributeEvent e) 对属性删除进行监听
attributeReplaced(ServletRequestAttributeEvent e) 对属性替换进行监听
拦截器:
第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口
第二种方式是继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter
HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
preHandle(): 这个方法在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
postHandle():这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
afterCompletion():该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
常见问题
监听器、拦截器、过滤器执行顺序?
监听器 >>拦截器>> 过滤器
扩展思考
监听器、拦截器、过滤器的区别?
1.过滤器(Filter):过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,这一点,是拦截器无法做到的。在Java Web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。filter 流程是线性的,url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收。
2.监听器(Listener):监听器,也是系统级别的监听。监听器随web应用的启动而启动。Java的监听器在c/s模式里面经常用到,它会对特定的事件产生产生一个处理。监听在很多模式下用到,比如说观察者模式,就是一个使用监听器来实现的,在比如统计网站的在线人数。又比如struts2可以用监听来启动。Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。
3.拦截器(Interceptor):java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性。Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现的动态代理。它依赖于具体的接口,在运行期间动态生成字节码。
拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或
者之后加入某些操作。java的拦截器主要是用在插件上,扩展件上比如 Hibernate Spring Struts2等,有点类似面向切片的技术,在用之前先要在配置文件即xml,文件里声明一段的那个东西。
参考文献
https://blog.csdn.net/reggergdsg/article/details/52962774
更多讨论
Q1:过滤器的执行顺序。
A1:<filter-mapping>中的配置执行顺序决定了过滤器的执行顺序。
Q2:拦截器是单例,因此不管用户请求多少次都只有一个拦截器实现,即线程不安全,那我们应该怎么记录时间呢?
A2:使用ThreadLocal,它是线程绑定的变量,提供线程局部变量(一个线程一个ThreadLocal,A线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal)。
Q3:监听器不能与所监听的对象在同一个线程内?
A3:监听器不能与所监听的对象在同一个线程内,否则监听不了。