Servlet Filtering 过滤器(filter)是Java类,可以改变请求(request)和响应(response)的头信息与内容信息。过滤器不同于其他Web组件的地方是它本身并不创建响应(response),然而它可以依附在任何类型的Web资源上。过滤器截取请求(request),检查和改变request对象、response对象,并可以执行一些其他的任务。过滤器提供的主要功能是:
过滤器截获对特定命名的一个资源和一组资源的请求(request),然后执行过滤器中的代码。对于特定的资源,可以指定按照一定顺序调用的一个和多个过滤器,这就组成了链(chain)。使用过滤器主要包括:
编写过滤器的API是javax.servlet包中Filter、FilterChain和FilterConfig接口中定义的一些方法。定义一个过滤器就是实现Filter接口。Filter接口中最主要的方法是doFilter()方法,它接收三个参数:request对象、response对象、filterchain对象。 void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)这个方法能够执行的动作包括:
除了doFilter()方法,开发人员也必须实现init()和destroy()方法。当容器创建过滤器实例时调用init()方法, void init(FilterConfig filterConfig)可以从FilterConfig对象中获得初始化参数。 在doFilter()方法中,过滤器可以从FilterConfig对象获得ServletContext对象,那么就可以访问存储在ServletContext中的属性对象。当过滤器完成特定的处理过程后,调用chain对象的doFilter()方法。例如 public final class HitCounterFilter implements Filter { private FilterConfig filterConfig = null; // 初始化 public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } // 结束 public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) return; StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); writer.println(' ... '); . . . writer.flush(); // 输出日志 filterConfig.getServletContext(). log(sw.getBuffer().toString()); ... //调用在过滤器链中的下一个实体 chain.doFilter(request, wrapper); ... }}6.5.1.2. 定制请求和响应有许多方法可以改变请求(request)或者响应(response)。例如过滤器能够给请求(request)增加属性或在响应(response)中插入数据。 过滤器如果需要改变响应(response)必须在响应(response)返回给客户端之前捕获它。要做到这一点需要传递“替身”流(stream)给Servlet,然后利用“替身”stream产生响应(response)。“替身”stream防止了当响应(response)结束后关闭了原始的响应(response)输出流,并且允许过滤器改变Servlet的响应(response)。 过滤器产生“替身”stream
为了给Servlet传递“替身”stream,过滤器需要创建response对象的包装类并且覆盖getWriter或getOutputStream方法。包装类被FilterChain对象的doFilter方法传递。创建请求(request)的包装类继承ServletRequestWrapper或HttpServletRequestWrapper,创建响应(response)的包装类继承ServletResponseWrapper或HttpServletResponseWrapper。 以下CharResponseWrapper类包装了响应(response): public class CharResponseWrapper extends HttpServletResponseWrapper { private CharArrayWriter output; public String toString() { return output.toString(); } public CharResponseWrapper(HttpServletResponse response){ super(response); output = new CharArrayWriter(); } public PrintWriter getWriter(){ return new PrintWriter(output); }}包装类被传递给BookStoreServlet,BookStoreServlet把响应写入“替身”stream,当chain.doFilter返回,HitCounterFilter重新找回response把它写入缓冲,过滤器插入计数器值到缓冲中然后重新设置response的头信息,最后把缓冲中的内容写入response。 PrintWriter out = response.getWriter();//构造包装类CharResponseWrapper wrapper = new CharResponseWrapper( (HttpServletResponse)response);//向 doFilter 传递包装类chain.doFilter(request, wrapper);CharArrayWriter caw = new CharArrayWriter();caw.write(wrapper.toString().substring(0, wrapper.toString().indexOf('</body>')-1));...caw.write('\n</body></html>');//重新设置响应长度response.setContentLength(caw.toString().length());out.write(caw.toString());out.close();6.5.1.3. 映射过滤器Web容器使用过滤器映射来决定是否过滤Web资源。在Web应用的部署描述文件中映射过滤器到Servlet或URL模板。
如下图,可以映射一个过滤器到一个或多个Servlet,或者可以映射一个 Servlet到多个过滤器。 过滤器的映射
过滤器F1映射到servlet S1,S2和S3,过滤器F2映射到servlet S2,过滤器F3 映射到servlet S1和S2。 6.5.2. Application Events应用事件模型提供了当ServletContext,HttpSession,ServletRequest状态改变时的通知功能。可以编写事件监听类来响应这些状态的改变,并且可以配置和部署应用事件和监听类到Web应用。 对于ServletContext事件,当Web应用部署、卸载和对context增加属性时,事件监听类可以得到通知。下表列出了ServletContext的事件类型,对应特定事件的监听类必须实现的接口和当事件发生时调用的方法。 事件类型 接口 方法 Servlet context被创建 javax.servlet.ServletContextListener contextInitialized() Servlet context被注销 javax.servlet.ServletContextListener contextDestroyed() 增加属性 javax.servlet. ServletContextAttributesListener attributeAdded() 删除属性 javax.servlet. ServletContextAttributesListener attributeRemoved() 属性被替换 javax.servlet. ServletContextAttributesListener attributeReplaced()对于HttpSession事件,当session激活、删除或者session属性的增加、删除和替换时,事件监听类得到通知。下表列出了HttpSession的事件类型,对应特定事件的监听类必须实现的接口和当事件发生时调用的方法。 事件类型 接口 方法 session激活 javax.servlet.http. HttpSessionListener sessionCreated() session删除 javax.servlet.http. HttpSessionListener sessionDestroyed() 增加属性 javax.servlet.http. HttpSessionAttributesListener attributeAdded() 删除属性 javax.servlet.http. HttpSessionAttributesListener attributeRemoved() 属性被替换 javax.servlet.http. HttpSessionAttributesListener attributeReplaced()对于ServletRequest事件,当request初始化、销毁或者request属性的增加、删除和替换时,事件监听类得到通知。下表列出了ServletRequest的事件类型,对应特定事件的监听类必须实现的接口和当事件发生时调用的方法。 事件类型 接口 方法 session初始化 javax.servlet.ServletRequestListener requestInitialized() session销毁 javax.servlet.ServletRequestListener requestDestroyed() 增加属性 javax.servlet.ServletRequestAttributeListener attributeAdded() 删除属性 javax.servlet.ServletRequestAttributeListener attributeRemoved() 属性被替换 javax.servlet.ServletRequestAttributeListener attributeReplaced() 6.5.2.1. 配置事件监听类配置事件监听类的步骤:
编写事件监听类的步骤:
ServletContext 监听类例子: import javax.servlet.*;public final class myContextListenerClass implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { /* 当 ServletContext 初始化时被调用,可以在这儿 初始化 ServletContext 的相关数据 */ } public void contextDestroyed(ServletContextEvent event) { /* 当 Web 应用被卸载或 Apusic 服务器关闭时被调用 */ }}HttpSession 属性监听类例子: import javax.servlet.*;public final class mySessionAttributeListenerClass implements HttpSessionAttributesListener { public void attributeAdded(HttpSessionBindingEvent sbe) { /* 增加session属性时被调用 */ } public void attributeRemoved(HttpSessionBindingEvent sbe) { /* 删除session属性时被调用 */ } public void attributeReplaced(HttpSessionBindingEvent sbe) { /* 替换session属性时被调用 */ }}ServletRequest 属性监听类例子: import javax.servlet.*;public final class myRequestAttributeListenerClass implements ServletRequestAttributeListener { public void attributeAdded(HttpSessionBindingEvent sbe) { /* 增加request属性时被调用 */ } public void attributeRemoved(HttpSessionBindingEvent sbe) { /* 删除request属性时被调用 */ } public void attributeReplaced(HttpSessionBindingEvent sbe) { /* 替换request属性时被调用 */ }}----------------------------------------------- ----------------------------------------------- Servlet和Filter的url匹配以及url-pattern详解Servlet和filter是J2EE开发中常用的技术,使用方便,配置简单,老少皆宜。估计大多数朋友都是直接配置用,也没有关心过具体的细节,今天遇到一个问题,上网查了servlet的规范才发现,servlet和filter中的url-pattern还是有一些文章在里面的,总结了一些东西,放出来供大家参考,以免遇到问题又要浪费时间。 一,servlet容器对url的匹配过程:
当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/test/aaa.html,我的应用上下文是test,容器会将http://localhost/test去掉,剩下的/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了(filter不同,后文会提到)。其匹配规则和顺序如下: 1. 精确路径匹配。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先 进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。 2. 最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。 3. 扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action 4. 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet(什么是default servlet?后面会讲)。
根据这个规则表,就能很清楚的知道servlet的匹配过程,所以定义servlet的时候也要考虑url-pattern的写法,以免出错。 对于filter,不会像servlet那样只匹配一个servlet,因为filter的集合是一个链,所以只会有处理的顺序不同,而不会出现只选择一个filter。Filter的处理顺序和filter-mapping在web.xml中定义的顺序相同。 二,url-pattern详解在web.xml文件中,以下语法用于定义映射: l 以”/’开头和以”/*”结尾的是用来做路径映射的。 l 以前缀”*.”开头的是用来做扩展映射的。 l “/” 是用来定义default servlet映射的。 l 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action 所以,为什么定义”/*.action”这样一个看起来很正常的匹配会错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。 |
|
来自: JourneyHappy > 《待分类》