分享

servlet filter 详解_Step-By-Step_百度空间

 JourneyHappy 2014-12-01
Servlet Filtering

过滤器(filter)是Java类,可以改变请求(request)和响应(response)的头信息与内容信息。过滤器不同于其他Web组件的地方是它本身并不创建响应(response),然而它可以依附在任何类型的Web资源上。过滤器截取请求(request),检查和改变request对象、response对象,并可以执行一些其他的任务。过滤器提供的主要功能是:

  • 实现日志功能

  • 实现用户定义的安全功能

  • 调试功能

  • 加密

  • 数据压缩

  • 改变发送给客户端的响应(response)

过滤器截获对特定命名的一个资源和一组资源的请求(request),然后执行过滤器中的代码。对于特定的资源,可以指定按照一定顺序调用的一个和多个过滤器,这就组成了链(chain)。使用过滤器主要包括:

  • 编写过滤器类

  • 定制请求(request)和响应(response)

  • 为特定的Web资源指定过滤器链

6.5.1.1. 编写过滤器类

编写过滤器的API是javax.servlet包中Filter、FilterChain和FilterConfig接口中定义的一些方法。定义一个过滤器就是实现Filter接口。Filter接口中最主要的方法是doFilter()方法,它接收三个参数:request对象、response对象、filterchain对象。

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

这个方法能够执行的动作包括:

  • 检查请求(request)的头信息

  • 定制request对象,改变请求(request)的头信息或数据

  • 定制response对象,改变响应(response)的头信息或数据

  • 调用在过滤器链中的下一个实体。如果当前过滤器是链中的最后一个过滤器,那么下一个实体就是客户请求(request)的资源;否则,链中的下一个过滤器会被调用。通过chain对象的doFilter()方法调用下一个实体,并传递request对象和response对象作为参数。另外,也可以不调用doFilter()方法阻塞请求(request),这样,过滤器应该负责填充对客户的响应(response)。

  • 检查响应的头信息

  • 抛出异常显示处理过程中的错误

除了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模板。

  • 在部署描述文件中加入<filter>标记 ,此标记包括:

    1. <filter-name>:过滤器名称

    2. <filter-class>:过滤器的实现类

    3. <init-params>:过滤器的初始参数

    <filter> <filter-name>Compression Filter</filter-name> <filter-class>CompressionFilter</filter-class> <init-param> <param-name>compressionThreshold</param-name> <param-value>10</param-value> </init-param></filter>

  • 在部署描述文件中加入<filter-mapping>标记,映射过滤器到Servlet:

    <filter-mapping> <filter-name>Compression Filter</filter-name> <servlet-name>CompressionTest</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher></filter-mapping><servlet> <servlet-name>CompressionTest</servlet-name> <servlet-class>CompressionTest</servlet-class></servlet><servlet-mapping> <servlet-name>CompressionTest</servlet-name> <url-pattern>/CompressionTest</url-pattern></servlet-mapping>

    映射过滤器到URL模板:

    <filter> <filter-name>HitCounterFilter</filter-name> <filter-class>HitCounterFilter</filter-class></filter><filter-mapping> <filter-name>HitCounterFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher></filter-mapping>

    使用<url-pattern>/*<url-pattern>标记,此过滤器将使用于此web应用中的任何静态资源或Servlet内容,因为任何URL都可匹配'/*'的URL模式。

  • dispatcher元素的作用 :Servlet 2.4版的Web程序增强了filter和request dispatcher的配合功能,这样过滤器可以根据请求分发器(request dispatcher)所使用的方法有条件地对Web请求进行过滤。

    1. 只有当request直接来自客户,过滤器才生效,对应为REQUEST条件。

    2. 只有当request被一个请求分发器使用forward()方法转到一个Web构件时,对应称为FORWARD条件。

    3. 只有当request被一个请求分发器使用include()方法转到一个Web构件时,对应称为INCLUDE条件。

    4. 只有当request被一个请求分发器使用“错误信息页”机制方法转到一个Web构件时,对应称为ERROR条件。

    5. 第五种过滤器作用的条件可以是上面四种条件的组合。

    6. 当不使用dispatcher元素时,客户的直接request会被用来过滤请求。如果请求是从一个request dispatcher转发过来的,这个过滤器不工作。

如下图,可以映射一个过滤器到一个或多个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. 配置事件监听类

配置事件监听类的步骤:

  1. 打开Web应用的部署描述文件web.xml

  2. 增加事件声明标记<listener>。事件声明定义的事件监听类在事件发生时被调用。<listener>标记必须在<filter>和<filter-mapping>标记之后和<servlet>标记之前。可以为每种事件定义多个事件监听类,Apusic应用服务器按照它们在部署描述文件声明的顺序调用。例如:

    <listener> <listener-class>myApp.myContextListenerClass</listener-class></listener><listener> <listener-class>myApp.mySessionAttributeListenerClass</listener-class></listener>

  3. 编写和部署监听类。

6.5.2.2. 编写事件监听类

编写事件监听类的步骤:

  1. 创建新的类并实现事件对应的接口

  2. 定义不接受参数、访问属性为public的构造函数

  3. 实现接口的方法

  4. 编译并拷贝到对应Web应用的WEB-INF/classes目录下,或者打包成jar文件拷贝到WEB-INF/lib目录下

6.5.2.3. 事件监听类模板

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”这样一个看起来很正常的匹配会错?因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多