Servlet之过滤器详解


一、过滤器简介

       servlet API中最重要的一个功能就是可以为servlet和JSP页面定义过滤器。过滤器提供了某些早期服务器所支持的非标准“servlet连接”的一种功能强大且标准的替代品。 java

       过滤器是一个程序,它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上,而且能够检查进入这些资源的请求信息。在这以后,过滤器能够做以下的选择: web

  1. 以常规的方式调用资源(即,调用servlet或JSP页面);
  2. 利用修改过的请求信息调用资源;
  3. 调用资源,但在发送响应到客户机前对其进行修改;
  4. 阻止该资源调用,代之以转到其余的资源,返回一个特定的状态代码或生成替换输出;

       过滤器提供了几个重要好处:数据库

  1. 它以一种模块化的或可重用的方式封装公共的行为。你有30个不一样的serlvet或JSP页面,须要压缩它们的内容以减小下载时间吗?没问题:构造一个压缩过滤器,而后将它应用到30个资源上便可;安全

  2. 利用它可以将高级访问决策与表现代码相分离。这对于JSP特别有价值,其中通常但愿将几乎整个页面集中在表现上,而不是集中在业务逻辑上。例如,但愿阻塞来自某些站点的访问而不用修改各页面(这些页面受到访问限制)吗?没问题:创建一个访问限制过滤器并把它应用到想要限制访问的页面上便可;服务器

  3. 过滤器使你可以对许多不一样的资源进行批量性的更改。你有许多现存资源,这些资源除了公司名要更改外其余的保持不变,能办到么?没问题:构造一个串替换过滤器,只要合适就使用它。cookie

       注意:app

       过滤器只在与servlet规范2.3版兼容的服务器上有做用。若是你的Web应用须要支持旧版服务器,就不能使用过滤器。jsp

二、过滤器的基本工做原理

  • 当在 web.xml 中注册了一个 Filter 来对某个 Servlet 程序进行拦截处理时,这个 Filter 就成了 Servlet 容器与该 Servlet 程序的通讯线路上的一道关卡,该 Filter 能够对 Servlet 容器发送给 Servlet 程序的请求和 Servlet 程序回送给 Servlet 容器的相应进行拦截,能够决定是否将请求继续传递给 Servlet 程序,以及对请求和相应信息是否进行修改;svg

  • 在一个 web 应用程序中能够注册多个 Filter 程序,每一个 Filter 程序均可以对一个或一组 Servlet 程序进行拦截;模块化

  • 如有多个 Filter 程序对某个 Servlet 程序的访问过程进行拦截,当针对该 Servlet 的访问请求到达时,web 容器将把这多个 Filter 程序组合成一个 Filter 链(过滤器链)。Filter 链中各个 Filter 的拦截顺序与它们在应用程序的 web.xml 中映射的顺序一致。

三、过滤器的执行过程

四、过滤器的生命周期

  1. Web应用启动时建立过滤器实例并对其进行初始化操做;

  2. 对于过滤范围内资源的每次访问都将调用doFilter方法来完成相关操做;

  3. 应用被卸载时会调用destory方法,销毁过滤器实例。

五、过滤器的编写步骤

       编写一个过滤器涉及下列五个步骤:

  1. 创建一个实现Filter接口的类。这个类须要三个方法,分别是:doFilter、init和destroy; doFilter方法包含主要的过滤代码(见第2步),init方法创建设置操做,而destroy方法完成对象的销毁操做;

  2. 在doFilter方法中放入过滤行为。doFilter方法的第一个参数为ServletRequest对象;此对象给过滤器提供了对进入的信息(包括表单数据、cookie和HTTP请求头)的彻底访问;第二个参数为ServletResponse,一般在简单的过滤器中忽略此参数;最后一个参数为FilterChain,以下一步所述,此参数用来调用servlet或JSP页;

  3. 调用FilterChain对象的doFilter方法。Filter接口的doFilter方法取一个FilterChain对象做为它的一个参数;在调用此对象的doFilter方法时,激活下一个相关的过滤器;若是没有另外一个过滤器与servlet或JSP页面关联,则servlet或JSP页面被激活;

  4. 对相应的servlet和JSP页面注册过滤器。在部署描述符文件(web.xml)中使用filter和filter-mapping元素;

  5. 禁用激活器servlet。防止用户利用缺省servlet URL绕过过滤器设置。

创建一个实现Filter接口的类

       全部过滤器都必须实现javax.servlet.Filter。这个接口包含三个方法,分别为doFilter、init和destroy。

public void doFilter(ServletRequset request, ServletResponse response,  FilterChain chain) thows ServletException, IOException;

       每当调用一个过滤器(即,每次请求与此过滤器相关的servlet或JSP页面)时,就执行其doFilter方法。正是这个方法包含了大部分过滤逻辑。第一个参数为与传入请求有关的ServletRequest。对于简单的过滤器,大多数过滤逻辑是基于这个对象的。若是处理HTTP请求,而且须要访问诸如getHeader或getCookies等在ServletRequest中没法获得的方法,就要把此对象构形成HttpServletRequest。

       第二个参数为ServletResponse。除了在两个情形下要使用它之外,一般忽略这个参数。首先,若是但愿彻底阻塞对相关servlet或JSP页面的访问。可调用response.getWriter并直接发送一个响应到客户机。其次,若是但愿修改相关的servlet或JSP页面的输出,可把响应包含在一个收集全部发送到它的输出的对象中。而后,在调用serlvet或JSP页面后,过滤器可检查输出,若是合适就修改它,以后发送到客户机。

       DoFilter的最后一个参数为FilterChain对象。对此对象调用doFilter以激活与servlet或JSP页面相关的下一个过滤器。若是没有另外一个相关的过滤器,则对doFilter的调用激活servlet或JSP自己。

public void init(FilterConfig config) thows ServletException;

       init方法只在此过滤器第一次初始化时执行,不是每次调用过滤器都执行它。对于简单的过滤器,可提供此方法的一个空体,但有两个缘由须要使用init。首先,FilterConfig对象提供对servlet环境及web.xml文件中指派的过滤器名的访问。所以,广泛的办法是利用init将FilterConfig对象存放在一个字段中,以便doFilter方法可以访问servlet环境或过滤器名.其次,FilterConfig对象具备一个getInitParameter方法,它可以访问部署描述符文件(web.xml)中分配的过滤器初始化参数。

public void destroy( );

       大多数过滤器简单地为此方法提供一个空体,不过,可利用它来完成诸如关闭过滤器使用的文件或数据库链接池等清除任务。

将过滤行为放入doFilter方法

       doFilter方法为大多数过滤器地关键部分。每当调用一个过滤器时,都要执行doFilter。对于大多数过滤器来讲,doFilter执行的步骤是基于传入的信息的。所以,可能要利用做为doFilter的第一个参数提供的ServletRequest。这个对象经常构造为HttpServletRequest类型,以提供对该类的更特殊方法的访问。

调用FilterChain对象的doFilter方法

       Filter接口的doFilter方法以一个FilterChain对象做为它的第三个参数。在调用该对象的doFilter方法时,激活下一个相关的过滤器。这个过程通常持续到链中最后一个过滤器为止。在最后一个过滤器调用其FilterChain对象的doFilter方法时,激活servlet或页面自身。 可是,链中的任意过滤器均可以经过不调用其FilterChain的doFilter方法中断这个过程。在这样的状况下,再也不调用JSP页面的serlvet,而且中断此调用过程的过滤器负责将输出提供给客户机。

对适当的servlet和JSP页面注册过滤器

       部署描述符文件的2.3版本引入了两个用于过滤器的元素,分别是:filter和filter-mapping。filter元素向系统注册一个过滤对象,filter-mapping元素指定该过滤对象所应用的URL。

filter元素

  • <filter>元素用于在Web应用程序中注册一个过滤器;

  • 在<filter>元素内:

<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空;
<init-param>元素用于为过滤器指定初始化参数,它的子元素;
<param-name>指定参数的名字;
<param-value>指定参数的值。在过滤器中,可使用FilterConfig接口对象来访问初始化参数;

  • Servlet容器对部署描述符中声明的每个过滤器,只建立一个实例。与Servlet相似,容器将在同一个过滤器实例上运行多个线程来同时为多个请求服务,所以,开发过滤器时,也要注意线程安全的问题。
<filter>
    <filter-name>testFitler</filter-name>
    <filter-class>org.test.TestFiter</filter-class>
    <init-param>
        <param-name>word_file</param-name>  
        <param-value>/WEB-INF/word.txt</param-value>
    </init-param>
</filter>

filter-mapping元素

  • <filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可经过两种方式来指定:Servlet 名称和资源访问的请求路径( url样式);

<filter-name>:子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字;
<url-pattern>:设置 filter 所拦截的请求路径(过滤器关联的URL样式);
<servlet-name>:指定过滤器所拦截的Servlet名称;
<dispatcher>:指定过滤器所拦截的资源被 Servlet 容器调用的方式,能够是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST. 能够设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截;

  • <dispatcher>子元素能够设置的值及其意义:

REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。若是目标资源是经过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用;

INCLUDE:若是目标资源是经过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此以外,该过滤器不会被调用;

FORWARD:若是目标资源是经过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此以外,该过滤器不会被调用;

ERROR:若是目标资源是经过声明式异常处理机制调用时,那么该过滤器将被调用。除此以外,过滤器不会被调用;

<filter-mapping>
     <filter-name>testFilter</filter-name>
    <url-pattern>/test.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
   <filter-name>testFilter</filter-name>
   <url-pattern>/index.jsp</url-pattern>
   <dispatcher>REQUEST</dispatcher>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
  • 在同一个 web.xml 文件中能够为同一个 Filter 设置多个映射。若一个 Filter 链中屡次出现了同一个 Filter 程序,这个 Filter 程序的拦截处理过程将被屡次执行。

禁用激活器servlet

       在对资源应用过滤器时,可经过指定要应用过滤器的URL模式或servlet名来完成。若是提供servlet名,则此名称必须与web.xml的servlet元素中给出的名称相匹配。若是使用应用到一个serlvet的URL模式,则此模式必须与利用web.xml的元素servlet-mapping指定的模式相匹配。可是,多数服务器使用“激活器servlet”为servlet体统一个缺省的URL:http://host/WebAppPrefix/servlet/ServletName。须要保证用户不利用这个URL访问servlet(这样会绕过过滤器设置)。例如,假如利用filter和filter-mapping指示名为SomeFilter的过滤器应用到名为SomeServlet的servlet,则以下:

<filter> 
    <filter-name>SomeFilter</filter-name> 
    <filter-class>somePackage.SomeFilterClass</filter-class> 
</filter> 
<!-- ... --> 
<filter-mapping> 
    <filter-name>SomeFilter</filter-name> 
    <servlet-name>SomeServlet</servlet-name> 
</filter-mapping>

       接着,用servlet和servlet-mapping规定URL http://host/webAppPrefix/Blah 应该调用SomeSerlvet,以下所示:

<filter> 
    <filter-name>SomeFilter</filter-name> 
    <filter-class>somePackage.SomeFilterClass</filter-class> 
</filter> 
<!-- ... --> 
<filter-mapping> 
    <filter-name>SomeFilter</filter-name> 
    <servlet-name>/Blah</servlet-name> 
</filter-mapping>

       如今,在客户机使用URL http://host/webAppPrefix/Blah 时就会调用过滤器。过滤器不该用到http://host/webAppPrefix/servlet/SomePackage.SomeServletClass。尽管有关闭激活器的服务器专用方法。可是,可移植最强的方法时从新映射Web应用钟的/servlet模式,这样使全部包含此模式的请求被送到相同的servlet中。为了从新映射此模式,首先应该创建一个简单的servlet,它打印一条错误消息,或重定向用户到顶层页。而后,使用servlet和servlet-mapping元素发送包含/servlet模式的请求到该servlet。程序清单9-1给出了一个简短的例子。

       程序清单9-1 web.xml(重定向缺省servlet URL的摘录)

<?xml version="1.0" encoding="ISO-8859-1"?> 
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> 
<web-app> 
    <!-- ... --> 
    <servlet> 
        <servlet-name>Error</servlet-name> 
        <servlet-class>somePackage.ErrorServlet</servlet-class> 
    </servlet> 
    <!-- ... --> 
    <servlet-mapping> 
        <servlet-name>Error</servlet-name> 
        <url-pattern>/servlet/*</url-pattern> 
    </servlet-mapping> 
    <!-- ... --> 
</web-app>

六、典型应用

       未完待续… …