25. Filter

Filter

过滤客户端和服务器之间的请求。是 web 三大核心(Servlet【处理请求】,Filter【过滤请求,响应】,Listener【监听器】)组件之一。

三大核心组件的共同点:

  1. 实现某个接口
  2. 注册

简单示例

新建一个类,并且实现 javax.servlet.Filter 接口,那么这个类就是 Filter 的实现类,需要在 web.xml 进行注册,注册和 Servlet 类似。如下所示:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <!-- Filter 配置 -->
  <filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>com.itguigu.filter.TestFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/UserServlet</url-pattern>   <!-- filter 应用在 /UserServlet 上 -->
  </filter-mapping>

  <!-- Servlet 配置 -->
  <servlet>
    <description></description>
    <display-name>UserServlet</display-name>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.itguigu.servlet.UserServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/UserServlet</url-pattern>
  </servlet-mapping>
</web-app>

TestFilter.java

package com.itguigu.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class TestFilter implements Filter{
    @Override
    public void destroy() {
        System.out.println("销毁");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter");
        // 放行
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化");
    }
}

UserServlet.java

package com.itguigu.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class UserServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("处理用户请求");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

生命周期

Filter 生命周期

顺序 执行时间 执行次数
构造器 启动服务器时 执行一次
init 启动服务器时 执行一次
doFilter 过滤请求时 执行多次
destroy 关闭服务器时 执行一次

Servlet 生命周期

顺序 执行时间 执行次数
构造器 第一次接受到请求时 执行一次
init 第一次接受到请求时 执行一次
service 每次接受到请求时都会执行 执行多次
destroy 关闭服务器时 执行一次

工作原理

  1. 请求发送
  2. 执行过滤器中 doFilter 放行前代码
  3. 放行
  4. 执行 Servlet 代码,做出响应
  5. 执行过滤器中 doFilter 放行后代码
  6. 做出响应

多个 Filter 执行流程

遵循先进后出原则,即:请求 —> Filter1 放行前 —> Filter2 放行前 —> Filter3 放行前 —> 处理请求, 做出响应 —> Filter3 放行后 —> Filter2 放行后 —> Filter1 放行后 —> 响应

即执行顺序取决于 web.xml 中的 filter-mapping 的顺序。和 filter 无关,例如,下面示例中

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>com.itguigu.filter.TestFilter</filter-class>
  </filter>

  <servlet>
    <description></description>
    <display-name>UserServlet</display-name>
    <servlet-name>UserServlet</servlet-name>
    <servlet-class>com.itguigu.servlet.UserServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>UserServlet</servlet-name>
    <url-pattern>/UserServlet</url-pattern>
  </servlet-mapping>

  <filter>
    <display-name>UserServletFilter</display-name>
    <filter-name>UserServletFilter</filter-name>
    <filter-class>com.itguigu.filter.UserServletFilter</filter-class>
  </filter>

  <!-- 执行顺序由 filter-mapping 决定,即先执行 UserServletFilter 后执行 TestFilter -->
  <filter-mapping>
    <filter-name>UserServletFilter</filter-name>
    <url-pattern>/UserServlet</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/UserServlet</url-pattern>
  </filter-mapping>
</web-app>

URL 配置规则

精准匹配

方式1

<filter>
  <filter-name>TestFilter</filter-name>
  <filter-class>com.itguigu.filter.TestFilter</filter-class>
</filter> 
<filter-mapping>
  <filter-name>TestFilter</filter-name>
  <url-pattern>/UserServlet</url-pattern> <!-- 精准匹配某个 URL  -->
</filter-mapping>

方式2

<filter>
  <filter-name>TestFilter</filter-name>
  <filter-class>com.itguigu.filter.TestFilter</filter-class>
</filter> 
<filter-mapping>
  <filter-name>TestFilter</filter-name>
  <servlet-name>UserServlet</servlet-name> <!-- 通过 servlet-name 来精准匹配 -->
   <!--<url-pattern>/UserServlet</url-pattern> -->
</filter-mapping>

模糊匹配

前置模糊(*在最前面)

<filter>
  <filter-name>TestFilter</filter-name>
  <filter-class>com.itguigu.filter.TestFilter</filter-class>
</filter> 
<filter-mapping>
  <filter-name>TestFilter</filter-name>
  <url-pattern>*.jsp</url-pattern> <!-- 过滤所有 jsp -->
</filter-mapping>

后置模糊(*在最后面)

<filter>
  <filter-name>TestFilter</filter-name>
  <filter-class>com.itguigu.filter.TestFilter</filter-class>
</filter> 
<filter-mapping>
  <filter-name>TestFilter</filter-name>
  <url-pattern>/pages/user/*</url-pattern> <!-- 过滤 /pages/user 的所有 -->
</filter-mapping>

封装 HttpFilter

将 Filter 进行封装,封装为 HttpFilter。并重载 doFilter 方法,将 request 类型改为 HttpServletRequest。response 类型改为 HttpServletResponse。并将 doFilter 方法抽象化,让子类继承实现,并提供获取 FilterConfig 的方法。

package com.itguigu.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public abstract class HttpFilter implements Filter {
    private FilterConfig filterConfig;
  public HttpFilter() {}
    public void destroy() {}

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {    
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        // 调用下面抽象的方法
        doFilter(req, res, chain);
    }

    /**
     * 抽象方法
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    public abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException ;

    public void init(FilterConfig fConfig) throws ServletException {
        this.filterConfig = fConfig;
    }

    /**
     * 获取 FilterConfig 对象
     * @return
     */
    public FilterConfig getFilterConfig() {
        return filterConfig;
    }
}

使用

package com.itguigu.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class HttpFilterTest extends HttpFilter {

    /**
     * 实现抽象方法
     */
    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 获取 Config
        FilterConfig filterConfig2 = this.getFilterConfig();
        // 做些操作
        System.out.println(filterConfig2);
        // 放行
        chain.doFilter(request, response);
    }
}

优化字符集

新建一个 CharSetFilter,并继承 HttpFilter

package com.itguigu.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CharSetFilter extends HttpFilter{
    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {
        // 设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=UTF-8");
        // 放行
        chain.doFilter(request, response);
    }  
}

将 CharSetFilter 过滤器应用到 / 下所有的请求中

  <filter>
    <display-name>CharSetFilter</display-name>
    <filter-name>CharSetFilter</filter-name>
    <filter-class>com.itguigu.filter.CharSetFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>CharSetFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

上面的编码格式在 CharSetFilter 是固定写死的。我们还可以在 xml 利用 init-param 进行设置

  <filter>
    <display-name>CharSetFilter</display-name>
    <filter-name>CharSetFilter</filter-name>
    <filter-class>com.itguigu.filter.CharSetFilter</filter-class>
    <!-- 设置编码,通过参数传递 -->
    <init-param>
        <param-name>code</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharSetFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
package com.itguigu.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CharSetFilter extends HttpFilter{
    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {
        // 获取编码
        FilterConfig filterConfig = this.getFilterConfig();
        String code = filterConfig.getInitParameter("code");
        // 设置编码
        request.setCharacterEncoding(code);
        response.setContentType("text/html;charset=UTF-8");
        // 放行
        chain.doFilter(request, response);
    }  
}

  目录