Java中Filter、Listener的学习以及istener、 filter、servlet 加载顺序详解

Filter

filter可认为是Servlet的一种“变种”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。它与Servlet的区别在于:它不能直接向用户生成响应。完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter有如下几个用处。

  • 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
  • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
  • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
  • 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

过滤器的基本原理
在Servlet作为过滤器使用时,它可以对客户的请求进行处理。处理完成后,它会交给下一个过滤器处理,这样,客户的请求在过滤链里逐个处理,直到请求发送到目标为止。例如,某网站里有提交“修改的注册信息”的网页,当用户填写完修改信息并提交后,服务器在进行处理时需要做两项工作:判断客户端的会话是否有效;对提交的数据进行统一编码。这两项工作可以在由两个过滤器组成的过滤链里进行处理。当过滤器处理成功后,把提交的数据发送到最终目标;如果过滤器处理不成功,将把视图派发到指定的错误页面。

下面先介绍一个简单的记录日志的Filter,这个Filter负责拦截所有的用户请求,并将请求的信息记录在日志中。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">代码   
  2.   
  3. public class LogFilter implements Filter   
  4. {  
  5. //FilterConfig可用于访问Filter的配置信息  
  6. private FilterConfig config;  
  7. //实现初始化方法  
  8. public void init(FilterConfig config)  
  9. {  
  10. this.config = config;   
  11. }  
  12. //实现销毁方法  
  13. public void destroy()  
  14. {  
  15. this.config = null;   
  16. }  
  17. //执行过滤的核心方法  
  18. public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException  
  19. {  
  20. //---------下面代码用于对用户请求执行预处理---------  
  21. //获取ServletContext对象,用于记录日志  
  22. ServletContext context = this.config.getServletContext();   
  23. long before = System.currentTimeMillis();  
  24. System.out.println("开始过滤...");  
  25. //将请求转换成HttpServletRequest请求  
  26. HttpServletRequest hrequest = (HttpServletRequest)request;  
  27. //记录日志  
  28. context.log("Filter已经截获到用户的请求地址: " + hrequest.getServletPath());  
  29. //Filter只是链式处理,请求依然放行到目的地址  
  30. chain.doFilter(request, response);   
  31. //---------下面代码用于对服务器响应执行后处理---------  
  32. long after = System.currentTimeMillis();  
  33. //记录日志  
  34. context.log("过滤结束");  
  35. //再次记录日志  
  36. context.log("请求被定位到" + hrequest.getRequestURI() + "所花的时间为: " + (after - before));   
  37. }  
  38. }</span>  

上面程序实现了doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

Filter生命周期同Servlet 的生命周期一样

    (1) 初始化
  在下列时刻装入 Servlet:
 如果已配置自动装入选项,则在启动服务器时自动装入
 在服务器启动后,客户机首次向 Servlet 发出请求时
 重新装入 Servlet 时装入 Servlet 后,服务器创建一个 Servlet 实例并且调用 Servlet 的 init() 方法。在初始化阶段,Servlet 初始化参数被传递给 Servlet 配置对象。
  (2) 请求处理
  对于到达服务器的客户机请求,服务器创建特定于请求的一个“请求”对象和一个“响应”对象。服务器调用 Servlet 的 service() 方法,该方法用于传递“请求”和“响应”对象。service() 方法从“请求”对象获得请求信息、处理该请求并用“响应”对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求,例如 doGet()、doPost() 或其它的方法。

   (3) 终止
  当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时,服务器会调用 Servlet 的 destroy() 方法。

Listener

它是基于观察者模式设计的,Listener 的设计对开发 Servlet 应用程序提供了一种快捷的手段,能够方便的从另一个纵向维度控制程序和数据。目前 Servlet 中提供了 5 种两类事件的观察者接口,它们分别是:4 个 EventListeners 类型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和 2 个 LifecycleListeners 类型的,ServletContextListener、HttpSessionListener。如下图所示:

 

Listener是Servlet的监听器,它可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,比如监听在线的用户的数量。当增加一个HttpSession时,就激发sessionCreated(HttpSessionEvent se)方法,这样就可以给在线人数加1。常用的监听接口有以下几个:

ServletContextAttributeListener监听对ServletContext属性的操作,比如增加、删除、修改属性。

ServletContextListener监听ServletContext。当创建ServletContext时,激发contextInitialized(ServletContextEvent sce)方法;当销毁ServletContext时,激发contextDestroyed(ServletContextEvent sce)方法。

HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发session Created(HttpSessionEvent se)方法;当销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se)方法。

HttpSessionAttributeListener监听HttpSession中的属性的操作。当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent se) 方法;当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent se)方法;当在Session属性被重新设置时,激发attributeReplaced(HttpSessionBindingEvent se) 方法。

下面我们开发一个具体的例子,这个监听器能够统计在线的人数。在ServletContext初始化和销毁时,在服务器控制台打印对应的信息。当ServletContext里的属性增加、改变、删除时,在服务器控制台打印对应的信息。

要获得以上的功能,监听器必须实现以下3个接口:

HttpSessionListener

ServletContextListener

ServletContextAttributeListener

 

例子:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.ee.listener;    
  2.     
  3. import javax.servlet.ServletContextAttributeEvent;    
  4. import javax.servlet.ServletContextAttributeListener;    
  5. import javax.servlet.ServletContextEvent;    
  6. import javax.servlet.ServletContextListener;    
  7. import javax.servlet.http.HttpSessionEvent;    
  8. import javax.servlet.http.HttpSessionListener;    
  9.     
  10. /**  
  11.  * @author Administrator  
  12.  *  
  13.  */    
  14. public class OnlineUserListener implements HttpSessionListener,    
  15.         ServletContextListener, ServletContextAttributeListener {    
  16.     private long onlineUserCount = 0;    
  17.     
  18.     public long getOnlineUserCount() {    
  19.         return onlineUserCount;    
  20.     }    
  21.     
  22.     /* (non-Javadoc)  
  23.      * @see javax.servlet.ServletContextAttributeListener#attributeAdded(javax.servlet.ServletContextAttributeEvent)  
  24.      */    
  25.     @Override    
  26.     public void attributeAdded(ServletContextAttributeEvent arg0) {    
  27.     
  28.     }    
  29.     
  30.     /* (non-Javadoc)  
  31.      * @see javax.servlet.ServletContextAttributeListener#attributeRemoved(javax.servlet.ServletContextAttributeEvent)  
  32.      */    
  33.     @Override    
  34.     public void attributeRemoved(ServletContextAttributeEvent arg0) {    
  35.     
  36.     }    
  37.     
  38.     /* (non-Javadoc)  
  39.      * @see javax.servlet.ServletContextAttributeListener#attributeReplaced(javax.servlet.ServletContextAttributeEvent)  
  40.      */    
  41.     @Override    
  42.     public void attributeReplaced(ServletContextAttributeEvent attributeEvent) {    
  43.         System.err.println("...attributeReplaced...");    
  44.     }    
  45.     
  46.     /* (non-Javadoc)  
  47.      * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)  
  48.      */    
  49.     @Override    
  50.     public void contextDestroyed(ServletContextEvent arg0) {    
  51.     
  52.     }    
  53.     
  54.     /* (non-Javadoc)  
  55.      * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)  
  56.      */    
  57.     @Override    
  58.     public void contextInitialized(ServletContextEvent arg0) {    
  59.     
  60.     }    
  61.     
  62.     /* (non-Javadoc)  
  63.      * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)  
  64.      */    
  65.     @Override    
  66.     public void sessionCreated(HttpSessionEvent httpSessionEvent) {    
  67.         onlineUserCount ++;    
  68.         toUpdateCount(httpSessionEvent);    
  69.     }    
  70.     
  71.     /* (non-Javadoc)  
  72.      * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)  
  73.      */    
  74.     @Override    
  75.     public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {    
  76.         onlineUserCount --;    
  77.         toUpdateCount(httpSessionEvent);    
  78.     }    
  79.     
  80.     private void toUpdateCount(HttpSessionEvent httpSessionEvent){    
  81.         httpSessionEvent.getSession().setAttribute("onlineUserCount", onlineUserCount);    
  82.     }    
  83. }    

listener、 filter、servlet 加载顺序

在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰。
首先可以肯定的是,加载顺序与它们在 web.xml 文件中的先后顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。最终得出的结论是:listener -> filter -> servlet

        同时还存在着这样一种配置节:context-param,它用于向 ServletContext 提供键值对,即应用程序上下文信息。我们的 listener, filter 等在初始化时会用到这些上下文中的信息,那么 context-param 配置节是不是应该写在 listener 配置节前呢?实际上 context-param 配置节可写在任意位置,因此真正的加载顺序为:context-param -> listener -> filter -> servlet

拦截器

拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。 
在Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。 
谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈 Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

拦截器的实现原理: 
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。


拦截器与过滤器的区别 :

1. 拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。 
3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值