JavaWeb Filter

Posted Mr.Aaron

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb Filter相关的知识,希望对你有一定的参考价值。

1. 过滤器概述

1.1. 什么是过滤器

Filter译为过滤器,是JavaWeb的三大组件之一,用于在Servlet之外对Request或者Response进行修改。对于Web应用程序来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器端之间的请求与响应信息。

1.2. 发展历史

由于Servlet规范是开放的,借助于公众与开源社区的力量,Servlet规范越来越科学,功能也越来越强大。2000年,Sun公司在Servlet2.3规范中添加了Filter功能,并在Servlet2.4中对Filter进行了细节上的补充。目前主流版本为Servlet2.5Filter

1.3. 运行原理

Servlet是服务器端用于处理客户端的请求与响应的,而Filter就是介于客户端与服务器端拦截客户端的请求或服务器端的响应,并对其修改或过滤。具体实现流程如下:

当客户端向服务器端发送一个请求时,如果有对应的过滤器进行拦截,过滤器可以改变请求的内容、或者重新设置请求协议的相关信息等,然后再将请求发送给服务器端的Servlet进行处理。当Servlet对客户端做出响应时,过滤器同样可以进行拦截,将响应内容进行修改或者重新设置后,再响应给客户端浏览器。在上述过程中,客户端与服务器端并不需要知道过滤器的存在。

在一个Web应用程序中,可以部署多个过滤器进行拦截,这些过滤器组成了一个过滤器链。过滤器链中的每个过滤器负责特定的操作和任务,客户端的请求在这些过滤器之间传递,直到服务器端的Servlet。具体执行流程如下:

1.4. 第一个过滤器

Servlet API中提供了一个Filter接口,实现过滤器只需要实现该接口即可。以下是Filter接口的API

Method Summary

void

destroy() 
Called by the web container to indicate to a filter that it is being taken out of service.

void

doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain.

void

init(FilterConfig filterConfig) 
Called by the web container to indicate to a filter that it is being placed into service.

实现过滤器的具体步骤如下:

  • 创建一个Java类,并实现Filter接口,重写该接口的方法。
public class MyFitler implements Filter {
    /**
     * init()方法用于Filter的初始化
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter的init()方法...");
    }
    /**
     * doFilter()方法用于Filter的拦截
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter的doFilter()方法...");
    }
    /**
     * destory()方法用于Filter的销毁
     */
    public void destroy() {
        System.out.println("执行了Filter的destroy()方法...");
    }
}
  • Web工程的web.xml文件中配置过滤器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <!-- 配置过滤器 -->
  <filter>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置对应过滤器类的完整路径 -->
      <filter-class>app.java.fitler.MyFitler</filter-class>
  </filter>
  <!-- 配置过滤器的拦截路径 -->
  <filter-mapping>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置过滤器的拦截的路径 -->
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>
  • 创建Web动态资源Servlet
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<h1>Hello Servlet.</h1>");
        out.flush();
        out.close();
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • Web工程的web.xml文件中配置Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>app.java.servlet.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>
  • 创建Web工程的静态资源JSP页面。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>hello.jsp</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  <body>
    <h1>Hello JSP.</h1>
  </body>
</html>
  • 发布Web工程并访问,无论是访问动态资源Servlet还是静态资源JSP,过滤器都会拦截,并执行过滤器的doFilter()方法。
  • 这时访问的Servlet或者JSP并没有被执行,原因是过滤器只进行了拦截,并没有将请求发送到对应的Servlet或者JSP。在过滤器的doFilter()方法中执行FilterChain对象的doFilter()方法将进行放行。
public class MyFitler implements Filter {
    /**
     * init()方法用于Filter的初始化
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter的init()方法...");
    }
    /**
     * doFilter()方法用于Filter的拦截
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter的doFilter()方法...");
        chain.doFilter(request, response);
    }
    /**
     * destory()方法用于Filter的销毁
     */
    public void destroy() {
        System.out.println("执行了Filter的destroy()方法...");
    }
}

2. 深入过滤器

2.1. 生命周期

Servlet API提供的Filter接口中含有三个方法,分别为init()doFilter()destroy()方法。该三个方式就是Filter的生命周期方法。

  • Filter的构造函数
  • Tomcat服务器启动时执行。
  • Filter的生命周期中只执行一次。
  • init(FilterConfig)方法
    • Tomcat服务器启动时执行。
    • Filter的生命周期中只执行一次。
    • 用于Filter的初始化工作。
  • doFilter(ServletRequest, ServletResponse, FilterChain)方法
    • 在每次拦截时执行。
    • Filter的生命周期中只执行多次。
    • 用于Filter的拦截处理工作。
  • destroy()方法
    • Tomcat服务器关闭时执行。
    • Filter的生命周期中只执行一次。
    • 用于Filter的销毁工作。

2.2. 过滤器链

在一个Web应用程序中,可以部署多个过滤器进行拦截,这些过滤器组成了一个过滤器链。完成过滤器链的功能,具体步骤如下:

  • 创建一个Java类,并实现Filter接口,重写该接口的方法。
public class MyFitler1 implements Filter {
    /**
     * init()方法用于Filter的初始化
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter1的init()方法...");
    }
    /**
     * doFilter()方法用于Filter的拦截
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter1的doFilter()方法...");
    }
    /**
     * destory()方法用于Filter的销毁
     */
    public void destroy() {
        System.out.println("执行了Filter1的destroy()方法...");
    }
}
  • 再创建一个Java类,并实现Filter接口,重写该接口的方法。
public class MyFilter2 implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter2的init()方法...");
    }
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter2的doFilter()方法...");
    }
    public void destroy() {
        System.out.println("执行了Filter2的destroy()方法...");
    }
}
  • Web工程的web.xml文件中配置过滤器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <!-- 配置过滤器 -->
  <filter>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置对应过滤器类的完整路径 -->
      <filter-class>app.java.fitler.MyFitler</filter-class>
  </filter>
  <!-- 配置过滤器的拦截路径 -->
  <filter-mapping>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置过滤器的拦截的路径 -->
      <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>
      <filter-name>MyFitler2</filter-name>
      <filter-class>app.java.fitler.MyFilter2</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>MyFitler2</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

需要注意的是,FilterChaindoFilter()方法执行时,如果只有一个过滤器的话,执行该方法会将请求发送给服务器端的动态或静态资源。如果是过滤器链的话,只有在执行过滤器链的最后一个过滤器的FilterChaindoFilter()方法时,才会将请求发送给服务器端的动态或静态资源。如果不是在过滤器链的最后一个过滤器的FilterChaindoFilter()方法时,将请求发送给下一个过滤器进行拦截。

在过滤器链中的过滤器执行的先后顺序是按照Web工程的web.xml文件配置过滤器的先后顺序被执行。

2.3. FilterConfig

在过滤器接口的init()方法中提供了FilterConfig参数,通过该参数可以获取web.xml配置过滤器的参数内容,或者获取ServletContext对象等。FilterConfig API内容如下:

Method Summary

String

getFilterName() 
Returns the filter-name of this filter as defined in the deployment descriptor.

String

getInitParameter(String name) 
Returns a String containing the value of the named initialization parameter, or null if the parameter does not exist.

Enumeration

getInitParameterNames() 
Returns the names of the filter\'s initialization parameters as an Enumeration of String objects, or an empty Enumeration if the filter has no initialization parameters.

ServletContext

getServletContext() 
Returns a reference to the ServletContext in which the caller is executing.

具体使用方式如下:

  • 创建一个Java类,并实现Filter接口,重写该接口的方法。
public class MyFitler implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter的init()方法...");
    }
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter的doFilter()方法...");
    }
    public void destroy() {
        System.out.println("执行了Filter的destroy()方法...");
    }
}
  • Web工程的web.xml文件中配置过滤器,并且设置初始化参数内容。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <filter>
      <filter-name>MyFitler</filter-name>
      <filter-class>app.java.fitler.MyFitler</filter-class>
      <init-param>
          <param-name>longestory</param-name>
          <param-value>http://www.baidu.com.com</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>MyFitler</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>
  • 在过滤器的init()方法中获取配置文件的初始化参数。
public class MyFitler implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("执行了Filter的init()方法...");
System.out.println(filterConfig.getInitParameter("longestory"));
    }
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了Filter的doFilter()方法...");
    }
    public void destroy() {
        System.out.println("执行了Filter的destroy()方法...");
    }
}

需要注意的是,通过getInitParameter()方法获取的初始化参数是私有参数。只有当前过滤器才能获取到,而其他过滤器并不能访问。如果配置全局初始化参数,可以使用<context-param>来配置,并使用ServletContext对象获取。

2.4. Filter映射配置

过滤器需要配置在web.xml中才能生效。一个过滤器需要配置<filter><filter-mapping>标签,例如如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <!-- 配置过滤器 -->
  <filter>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置对应过滤器类的完整路径 -->
      <filter-class>app.java.fitler.MyFitler</filter-class>
  </filter>
  <!-- 配置过滤器的拦截路径 -->
  <filter-mapping>
      <!-- 配置过滤器的名称 -->
      <filter-name>MyFitler</filter-name>
      <!-- 配置过滤器的拦截的路径 -->
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

<filter>配置过滤器的名称,实现类以及初始化参数。<filter-mapping>配置当前过滤器拦截的路径。<filter-mapping>中的<url-pattern>标签用于配置当前过滤器拦截的路径,配置方式与Servlet<url-pattern>配置方式类似,共有三种方式:

  • 完全匹配
  • 目录匹配
  • 扩展名匹配

如果需要拦截的是Servlet的话,有两种方式配置拦截路径:

  • 使用<url-pattern>标签:<url-pattern>/hello</url-pattern>
  • 使用<servlet-name>标签:<servlet-name>HelloServlet</servlet-name>

<dispatcher>标签配置到达Servlet的方法,有四种取值:REQUESTFORWARDINCLUDEERROR。可以同时配置多个<dispatcher>标签,如果没有配置<dispatcher>标签,默认为REQUEST。这四种取值的区别如下:

  • REQUEST:表示仅当直接请求Servlet时才生效。
  • FORWARD:表示仅当某Servlet通过FORWARD到该Servlet时才生效。
  • INCLUDEJSP中可以通过<jsp:include>标签请求某Servlet或调用RequestDispatcherinclude()方法请求某Servlet,仅这种情况下有效。
  • ERRORJSP中可以通过<%@ page errorPage=”error.jsp”>标签指定错误处理页面,仅这种情况下有效。

<url-pattern>标签与<dispatcher>标签的关系是“且”的关系。只有满足<url-pattern>标签的条件,且满足<dispatcher>标签的条件时,当前过滤器才能生效。

3. 过滤器案例

3.1. 全站乱码案例

中文乱码问题一直都是Web应用开发的问题,想要解决整个Web应用程序的中文乱码问题,可以如下操作:

  • 创建一个Java类继承于HttpServletRequestWrapper类,用于重写HttpServletRequest,解决GET方式的中文乱码问题。
public class MyRequest extends HttpServletRequestWrapper {
    public MyRequest(HttpServletRequest request) {
        super(request);
    }
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (getMethod().equalsIgnoreCase("GET")) {
            try {
                value = new String(value.getBytes("ISO-8859-1"),"utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return value;
    }
}
  • 创建一个过滤器用于解决整个Web应用程序的中文乱码问题。
public class EncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {}
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        MyRequest req = new MyRequest((HttpServletRequest)request);
        chain.doFilter(req, response);
    }
    public void destroy() {}
}
  • 配置Web工程的web.xml文件。