过滤器

概念

  在容器调用Servlet的service()方法前,Servlet并不会知道有请求的到来,而在Servlet的service()方法运行后,容器真正对浏览器进行HTTP响应之前,浏览器也不会知道Servlet真正的响应是什么。过滤器(Filter)正如其名称所示,是介于Servlet之前,可拦截过滤浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应
  性能评测、用户验证、字符替换、编码设置等需求,基本上与应用程序的业务需求没有直接的关系,只是应用程序额外的元件服务之一。你可能只是短暂需要它,或者需要整个系统应用相同设置,不应该为了一时的需要而修改代码强加入原有业务流程中。例如,性能的评测也许只是开发阶段才需要的,上线之后就要拿掉性能评测的功能,如果直接将性能评测的代码编写在业务流程中,那么要拿掉这个功能,就又得再修改一次源代码。因此,如性能评测、用户验证、字符替换、编码设置这类的需求,应该设计为独立的元件,随时可以加入应用程序中,也随时可以移除,或随时可以修改设置而不用修改原有的程序。这类元件就像是一个过滤器,安插在浏览器与Servlet中间,可以过滤请求与响应而作进一步的处理。并可以视需求抽换过滤器或调整过滤器的顺序,也可以针对不同的URL应用不同的过滤器。甚至在不同的Servlet间请求转发或包含时应用过滤器

小提示
  性能评测,记录请求与响应间的时间差。
  字符替换,假设有个留言版程序已经上线并正常运作中,但是现在发现,有些用户会在留言中输入一些HTML标签。基于安全性的考量,不希望用户输入的HTML标签直接出现在留言中而被浏览器当作HTML的一  部分。例如,并不希望用户在留言中输入< a href=" ">www.sina.com.cn</ a>这样的信息,不想信息在留言显示中直接变成超链接,让用户有机会在留言版中打广告。这时我们希望将一些HTML字符过滤掉,如将<、> 角括号置换为HTML实体字符&lt;与&gt;。 


声明

方法一:在实现HttpFilter的类上标注@WebFilter

@WebFilter("/*")
public class TestFilter extends HttpFilter {
    @Override
    protected void doFilter(
         HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws IOException, ServletException {
         // ...
    }
}
Java Code

方法二:在web.xml中声明

<web-app ...>
    <filter>
        <filter-name>testFilter</filter-name>
        <filter-class>com.Test.TestFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>testFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
Xml Code 

过滤策略的指定

指定过滤策略时,除了URL模式之外,也可以通过Servlet名称来指定,这可以通过@WebServlet的servletNames来设置。

方式一:注解方式

@WebFilter(servletNames={"SomeServlet"})
Java Code

方式二:web.xml方式,通过 <filter-mapping> 中的 <servlet-name> 标签來設定。下面的Demo会使用这种方式。

<filter-mapping>
    <filter-name>performance</filter-name>
    <servlet-name>SomeServlet</servlet-name>
</filter-mapping>
Xml Code

如果同时具备<url-pattern>与<servlet-name>,则先比对<url-pattern>,再比对<servlet-name>。


过滤器初始化参数设置

方式一:注解方式

@WebFilter(
    urlPatterns={"/*"}, 
    initParams={
        @WebInitParam(name = "PARAM1", value = "VALUE1"),
        @WebInitParam(name = "PARAM2", value = "VALUE2")
    }
)
public class TestFilter extends HttpFilter {
    private String PARAM1;
    private String PARAM2;

    @Override
    public void init() throws ServletException {
        PARAM1 = getInitParameter("PARAM1");
        PARAM2 = getInitParameter("PARAM2");
    }
    
    //...
}
Java Code

方式二:web.xml方式,web.xml中的设置会覆盖注解方式的设置

    <filter>
        <filter-name>TestFilter</filter-name>
        <filter-class>com.test.TestFilter</filter-class>
        <init-param>
            <param-name>PARAM1</param-name>
            <param-value>VALUE1</param-value>
        </init-param>
        <init-param>
            <param-name>PARAM2</param-name>
            <param-value>VALUE2</param-value>
        </init-param>
    </filter>
Xml Code

触发策略 

触发过滤器的时机,默认是浏览器发出请求。如果是那些通过RequestDispatcher的forward()或include()来转发或包含的请求,需要设置@WebFilter的dispatcherTypes

方式一:注解方式

@WebFilter(
    urlPatterns={"/some"}, 
    dispatcherTypes={
        DispatcherType.FORWARD, DispatcherType.INCLUDE, 
        DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC
    }
)
Java Code

方式二:web.xml方式

<filter-mapping>
    <filter-name>SomeFilter</filter-name>
    <servlet-name>*.do</servlet-name>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>
View Code

如果不设置任何dispatcherTypes,默认为REQUEST。
FORWARD是指通过RequestDispatcher的forward()而来的请求可以套用过滤器。
INCLUDE是指通过RequestDispatcher的include()而来的请求可以套用过滤器。
ERROR是指由容器处理例外而转发过来的请求可以触发过滤器。
ASYNC是指异步处理的请求可以触发过滤器。


过滤器执行顺序

如果有某个URL或Servlet会应用多个过滤器,则根据<filter-mapping>在web.xml中出现的先后顺序,来决定过滤器的运行顺序。


性能过滤器Demo

package com.test;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "testFilter")
public class TestFilter implements Filter {
    private FilterConfig filterConfig;
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long begin = System.currentTimeMillis();
        chain.doFilter(request, response);
        System.err.println("Request process in " + (System.currentTimeMillis() - begin) + " milliseconds");
    }
    public void destroy() { }
}
Filter Code  
package com.test;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/", name = "myServlet")
public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Servlet Code
<?xml version="1.1" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
  <display-name>Archetype Created Web Application</display-name>

  <filter-mapping>
    <filter-name>testFilter</filter-name>
    <servlet-name>myServlet</servlet-name>
  </filter-mapping>
</web-app>
web.xml Code

测试方法:

  1、浏览器访问

  2、查看控制台会输出字符串——“Request process in 3000 milliseconds”


API简介

  在doFilter()方法中进行service()方法的前置处理,而后决定是否调用FilterChain的doFilter()方法。如果调用了FilterChain的doFilter()方法,就会运行下一个过滤器,如果没有下一个过滤器了,就调用请求目标Servlet的service()方法。如果因为某个情况(如用户没有通过验证)而没有调用FilterChain的doFilter(),则请求就不会继续交给接下来的过滤器或目标Servlet,这时就是所谓的拦截请求。

  FilterChain的doFilter()实现,概念上类似以下代码:

       Filter filter = filterIterator.next();
        if (filter != null) {
            filter.doFilter(request, response, this);
        } else {
            targetServlet.service(request, response);
        }

  在陆续调用完Filter实例的doFilter()仍至Servlet的service()之后,流程会以堆栈顺序返回,所以在FilterChain的doFilter()运行完毕后,就可以针对service()方法做后续处理。


Servlet 4.0

  实现Filter接口是定义Filter的方式,然而,在Servlet4.0中,新增了GenericFilter类,目的类似于 GenericServlet,GenericFilter 將 FilterConfig 的設定、Filter 初始參數的取得做了封裝,并且在有参的init()方法里最后调用了一个无参的init()方法。
  同时也新增了 HttpFilter,繼承自 GenericFilter。
  因此,在 Servlet 4.0 中,若要定義 Filter,可以繼承 HttpFilter。

@WebFilter("/*")
public class TestFilter extends HttpFilter {
  @Override
  protected void doFilter(
    HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    throws IOException, ServletException {
      long begin = System.currentTimeMillis();
      chain.doFilter(request, response);
      getServletContext().log("Request process in " + (System.currentTimeMillis() - begin) + " milliseconds");
  }
}

原文地址:https://www.cnblogs.com/Mike_Chang/p/10063284.html