• 1. Servlet过滤器和监听器10/24/20181
  • 2. 过滤器的概念 过滤器链 创建过滤器 过滤器API 实现过滤器 部署过滤器 Servlet监听器 监听ServletContext对象事件 教学内容10/24/20182
  • 3. 1、过滤器的概念 过滤器是Servlet 2.3技术规范引入了一种新的Web应用程序组件。过滤器位于客户和Web应用程序之间,用于检查和修改两者之间流过的请求和响应。 过滤器作为一种Web应用程序组件,可以传输或者修改用户请求与servlet响应。它可以在用户请求到达servlet之前对请求进行处理,也可以在响应离开servlet之后修改响应信息。在请求到达servlet之前,过滤器可以截取该请求并检查请求内容。除了检查之外,还可以定制请求,如修改请求标题或者请求数据等。具体的方法是对传递过来的ServletRequest对象进行操作,达到检查和修改的目的。被过滤器处理后的请求发给servletservlet执行其任务并可能产生响应。 过滤器可以截获响应信息并进行修改,例如修改响应标题或者响应数据。具体的方法是通过操作servlet对象传递给它的ServletResponse对象来达到目的。处理完毕之后将修改后的响应信息发送给客户端。10/24/20183
  • 4. 在过滤器截获响应对象的时候,如果输出流被servlet关闭了,那么过滤器就不能够再改变输出流中的响应信息。因此如果需要修改响应信息,在servlet的实现代码中,应当是使用刷新输出流,而不能够关闭输出流。如: PrintWriter out=response.getPrintWriter(); … out.flush(); // 如果希望有过滤器截获并处理响应信息,此处不能用out.close()代替out.flush()。 10/24/20184
  • 5. 我们可以实现多个过滤器,这些过滤器就形成了一个“过滤器链”,过滤器链的实现与维护工作是由servlet容器负责实现的。 过滤器链中不同过滤器的先后顺序是在部署文件web.xml中设定的。最先截取客户请求的过滤器将最后才能截取servlet响应信息。 2、过滤器链10/24/20185
  • 6. 3、创建过滤器LogFilter过滤器是一个简单的Java类,主要特征是实现了Filter接口: package filters; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public final class ResponseTimeFilter implements Filter { : }10/24/20186
  • 7. FilterChain参数对于正确的过滤器操作至关重要,FilterChain对象代表了多个过滤器形成的过滤器链。为了将请求/响应沿过滤器链继续传送,在每个过滤器的doFilter方法中必须调用FilterChain对象的doFilter方法。Web容器调用本方法,说明过滤器正被嵌入到Web容器中去。容器只在实例化过滤器时才会调用该方法一次。初始化方法必须在被调用做过滤工作前正确完成。容器为这个方法传递一个FilterConfig对象,其中包含着与Filter相关的配置信息。每当请求和响应经过过滤器链时,容器都要调用一次该方法。需要注意的是过滤器的一个实例可以同时服务于多个请求,因此,特别需要注意多线程的同步问题。 4、过滤器API Filter接口:所有过滤器都必须实现javax.servlet.Filter接口。该接口定义了三个方法,这三个方法分别在过滤器生命周期的不同阶段被调用: public void init(FilterConfig filterConfig) public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) public void destroy()过滤器的doFilter()方法实现中,任何出现在FilterChain的doFilter方法之前的代码都被看作为预处理过滤器逻辑,在这一阶段,进入的请求是可用的,可在这里对请求进行修改,但不能修改响应信息,因为Web资源的响应处理还没有发生。 任何出现在FilterChain的doFilter()方法之后的代码构成了过滤器逻辑的后期处理。在这一阶段中,外发的Web资源的响应信息已经处理完毕,可以在这里修改响应信息。此时修改请求没有任何意义,因为请求已经处理完毕。 最后被调用的方法是destroy()方法。该方法同样由servlet容器调用,指出将从服务中删除该过滤器。如果过滤器使用了其他资源,需要在这个方法中释放这些资源。10/24/20187
  • 8. FilterConfig接口 当容器对Filter对象进行初始化时,容器调用Filter的init方法,并传入一个实现FilterConfig接口的对象。Filter可使用该对象获得一些有用的信息。FilterConfig接口包含以下方法:public String getFilterName() public String getInitParamter(String name) public Enumeration getInitParamterNames() public ServletContext getServletContext()获得过滤器的名称信息。该名称是在部署描述符中说明的。获得过滤器的初始化字符串。初始化字符串也是在部署描述符中说明的。获得一个枚举器,以遍历过滤器的所有初始化字符串。获得过滤器所在Web应用的ServletContext对象引用。10/24/20188
  • 9. FilterChain接口 对于一个Servlet,用户可以定义多个Filter。这些Filter由容器组织成一个过滤器链。在每个Filter对象中,可以使用容器传入doFilter方法的FilterChain参数引用该过滤器链。FilterChain接口定义了一个doFilter方法,用于将请求/响应继续沿链向后传送。 public void doFilter(ServletRequest req, ServletResponse res) 10/24/20189
  • 10. 5、实现过滤器ResponseTimeFilter ResponseTimeFilter的init方法的实现 //保存容器传递给过滤器的FilterConfig对象 FilterConfig filterConfig=null; //过滤器生成时由容器调用 public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig =filterConfig; //保存容器传来的FilterConfig对象的引用 }10/24/201810
  • 11. ResponseTimeFilter的Destroy方法的实现 //过滤器被销毁前由容器调用该方法,释放过滤器占用的资源 public void destroy() { filterConfig =null; //过滤器销毁之前释放filterConfig对象,进行垃圾回收 }10/24/201811
  • 12. LogFilter的DoFilter方法的实现 //当有请求来临时,请求通过本方法进入过滤器 public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException { long startTime=System.currentTimeMillis(); //访问时间戳 String remoteAddress=request.getRemoteAddr();//从请求获取远程地址 String remoteHost=request.getRemoteHost(); /从请求获取远程主机名 HttpServletRequest httpRequest=(HttpServletRequest)request; String reqURI=httpRequest.getRequestURI(); //客户请求的资源URI String browser=httpRequest.getHeader(“User-Agent”); //客户用的浏览器 chain.doFilter(request,response); long endTime= System.currentTimeMillis(); long workTime=endTime-startTime; ServletContext sc=filterConfig.getServletContext(); sc.log("Request from IP:" + remoteAddress+ "("+remoteHost+")" +"Using browser (" + browser+")" + " and wanted to access resource " +reqURI + " and used " + workTime + " ms"); }对请求对象进行以下预处理逻辑:存储当前时间,表示客户请求到达的时间;从ServletRequest对象中获取客户的远程地址、远程主机名、客户请求资源的URI,然后通过读取USER-AGENT标题获得客户端浏览器的类型。预处理逻辑完成后,必须调用下游过滤器。这只需要简单的调用FilterChain对象的doFilter()方法: chain.doFilter(request,response); 下游的doFilter()方法调用返回后,我们就可以处理后期逻辑了。后期逻辑处理。这时请求已经被目标servlet响应并产生了响应信息包装在ServletResponse对象中。如果有必要的话,可以在后续的代码中继续对响应信息进行处理,这时需注意,应保证不要使用Close方法将输出流关闭,而是使用Flush方法10/24/201812
  • 13. 6、部署过滤器 在创建过滤器之后必须将它添加到部署描述符中,这样容器才会将过滤器投入到服务中去。 配置工作由两部分组成。 声明过滤器 设置过滤器映射10/24/201813
  • 14. 声明过滤器 LogFilter filters.LogFilter filterVersion 1.0 copyright mycompany.com 过滤器名称。可使用FilterConfig的getFilterName方法获取。过滤器实现类的包路径。容器将在class目录下查找该类,以创建过滤器链。过滤器的初始化信息。过滤器对初始化参数的获取通常是在init()方法中进行,通过FilterConfig对象的getInitParameter()方法来实现。 过滤器的初始化信息可以出现多次。在Filter的初始化方法init中,可以使用getInitParameterName方法遍历所有的初始化信息。10/24/201814
  • 15. 设置过滤器映射。可采用两种方式进行 1、使用servlet-name标记将过滤器连接到一个servlet中 2、使用url-pattern将过滤器映射到某个URL模式 第二种方法会获得更大的灵活性。它能够使开发人员将过滤器应用于一组servlet、JSP或任何静态资源。 LogFilter myServlet copyrightFilter /*.jsp 10/24/201815
  • 16. Filter1 servlet1 Filter1 servlet2 Filter1 servlet3 Filter2 servlet2 Filter3 servlet1 Filter3 servlet2 Filter3 servlet3 10/24/201816
  • 17. 在进行过滤器组件的开发时,应注意以下原则: 过滤器逻辑与servlet逻辑不同,它不依赖于任何用户状态信息,因为一个过滤器实例可能同时处理多个完全不同的请求。 在映射过滤器时,应高度重视的顺序。一旦顺序颠倒,完全可能形成与设计时完全不同的结果。10/24/201817
  • 18. 7 Servlet监听器Servlet监听器是WEB应用程序事件模型的一部分,当WEB应用中的某些状态改变时, Servlet容器就会产生相应的事件, Servlet监听器用来处理这些事件. ServletContext初始化时产生ServletContextEvent事件 ServletRequest中添加删除属性时产生ServletRequestAttributeEvent事件 HttpSession对象创建和销毁时,容器产生HttpSessionEvent事件 参见书上表格 10/24/201818
  • 19. 生命周期 ServletContext的生命周期与Web应用的生命周期是一致的。 事件 在ServletContext生命周期中,一般会有很多事件发生。Servlet容器对ServletContext对象的操作,如初始化、销毁、添加或删除属性等,都会触发事件。8、监听ServletContext对象事件10/24/201819
  • 20. 侦听事件 如果Web应用程序需要监视Servlet的上下文环境的变更,那么就需要编写一个实现ServletContextListener接口的Java类; 若监视绑定到ServletContext对象上的属性的变化,那么就需要编写一个实现ServletContextAttributeListener接口的Java类; 需要用Servlet容器注册这些侦听类(即在web.xml文件中配置)10/24/201820
  • 21. ServletContextListener侦听器 可以为Web应用部署实现ServletContextListener接口的类来达到监视Web应用程序生命周期的目的。 该类称为ServletContextListener侦听器。 javax.servlet.ServletContextListener接口 public void contextInitialized(ServletContextEvent sce) 在Web应用的初始化阶段,servlet容器调用ServletContextListener对象的本方法,通知侦听器ServletContext对象进入初始化阶段。 public void contextDestroyed(ServletContextEvent sce) 在Web应用的结束阶段,servlet容器会调用ServletContextListener对象的本方法,通知侦听器ServletContext对象进入销毁阶段。 10/24/201821
  • 22. 启动Servlet容器时,Servlet容器为每个包含Servlet的Web应用实例化一个ServletContext对象启动Servlet容器时,Servlet容器还根据部署描述符,为每个Web应用实例化各种侦听器。随后,容器向其下所有Web应用的ServletContextlistener侦听器发送contextInitialized事件消息。 当Servlet容器关闭时,Servlet容器向容器中所有应用的ServletContextlistener侦听器发送contextDestroyed事件消息。10/24/201822
  • 23. ServletContextEvent对象 在上述两个事件中,容器通过传递ServletContextEvent对象来告诉侦听器发生的事件的具体信息。 ServletContextEvent是一个事件类。当Web应用程序启动或关闭时,Servlet容器将事件包装成ServletContextEvent对象,并将该对象作为参数传递给ServletContext对象的侦听器类的两个方法。 ServletContextEvent对象提供了一个getServletContext()方法,侦听器可以用它来获得对触发该事件的ServletContext对象的引用: public ServletContext getServletContext(); 10/24/201823
  • 24. public class ContextListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { ServletContext context=sce.getServletContext(); context.log(context.getServletContextName()+" initialated."); } public void contextDestroyed(ServletContextEvent sce) { ServletContext context=sce.getServletContext(); context.log("context.getServletContextName()+"destroyed."); } }ServletContext的初始化事件处理函数从事件对象sce中获取当前ServletContext对象的引用调用ServletContext的log方法,将ServletContext名字写入日志。10/24/201824
  • 25. ServletContextListener侦听器的部署: 在web.xml文件中,用元素进行定义,用子元素指定侦听器的实现类。 Servlet容器启动时,会根据web.xml文件内容,创建一个侦听器的实例并注册该侦听器。当ServletContext发生了相应的事件时就通知侦听器进行处理。 com.mycompany.servlet.ContextListener 10/24/201825
  • 26. ServletContextListener侦听器的测试: 1、在Tomcat中部署包含侦听器的Web应用; 2、启动Tomcat,则侦听器就会在日志文件中添加一行信息; 3、关闭Tomcat,侦听器同样会在日志文件中添加一行信息。: 2004-11-02 11:38:50 ContextLifeCycle initialated. : 2004-11-02 11:39:22 ContextLifeCycle destroyed. :10/24/201826
  • 27. 当调用ServletContext对象的setAttribute()方法改变已经绑定到ServletContext对象的某个属性的值的时候,容器会调用侦听器的该方法。ServletContextAttributeListener侦听器ServletContextAttributeListener侦听器 ServletContext的属性是由Web应用程序中所有的servlet所共享的。为保证属性在整个Web应用范围内的一致性,有必要监视ServletContext对象的任何属性的改变。 ServletContextAttributeListener侦听器就是为了这一目的而设立的。该侦听器是一个实现了接口ServletContextAttributeListener的Java类。 ServletContextAttributeListener接口 public void attributeAdded(ServletContextAttributeEvent scab) public void attributeRemoved(ServletContextAttributeEvent scab) public void attributeReplaced(ServletContextAttributeEvent scab)当调用ServletContext对象的setAttribute()方法将某个属性绑定到ServletContext对象时,容器会调用侦听器的该方法。当调用ServletContext对象的removeAttribute()方法将某个属性从ServletContext对象中删除时,容器会调用侦听器的这个方法。10/24/201827
  • 28. 当调用ServletContext对象的setAttribute()方法将某个属性绑定到ServletContext对象时,容器会调用侦听器的attributeAdded方法。当调用ServletContext对象的setAttribute()方法改变已经绑定到ServletContext对象的某个属性的值的时候,容器会调用侦听器的这个方法。当调用ServletContext对象的removeAttribute()方法将某个属性从ServletContext对象中删除时,容器会调用侦听器的这个方法。10/24/201828
  • 29. ServletContextAttributeEvent类 在上述三个事件中,容器通过传递ServletContextAttributeEvent对象来告诉侦听器发生的事件的具体信息。 ServletContextAttributeEvent类是一个事件类。 当Servlet容器捕获对ServletContext属性的操作事件时,它将事件包装成一个ServletContextAttributeEvent对象,并将该对象作为参数传递给属性侦听器类的三个方法。 ServletContextAttributeEvent类继承自ServletContextEvent类。 除了getServletContext()方法,还包含 String getName() 返回正在创建、替换或者删除的属性的名称 Object getValue() 返回正在创建、替换或者删除的属性的对象值。10/24/201829
  • 30. public class ContextListener implements ServletContextAttributeListener { public void attributeAdded(ServletContextAttributeEvent scab) { ServletContext context= scab.getServletContext(); context.log(scab.getName() + “ set to "+scab.getValue().toString() ); } public void attributeRemoved(ServletContextAttributeEvent scab) { ServletContext context= scab.getServletContext(); context.log(scab.getName() + " removed."); } public void attributeReplaced(ServletContextAttributeEvent scab) { ServletContext context= scab.getServletContext(); context.log(scab.getName() + " replaced to " + scab.getValue().toString()); } }当调用ServletContext对象的setAttribute()方法将某个属性绑定到ServletContext对象时,容器会调用侦听器的该方法。获取ServletContext对象的引用利用获得的ServletContext对象,将事件对象的信息写入日志。10/24/201830
  • 31. ServletContextAttributeListener侦听器的部署 在web.xml文件中,用元素进行定义,用子元素指定侦听器的实现类。 com.mycompany.servlet.MyAttributeListener 10/24/201831