什么是 Servlet 过滤器?
Servlet 过滤器是 Java Web 应用中的一种强大组件,它能够拦截客户端的请求和服务器的响应,在请求到达目标 Servlet 或 JSP 之前,过滤器可以对请求进行检查、修改或处理;在响应返回给客户端之前,过滤器也可以对响应进行处理。

你可以把它想象成一个安检门或者中间人:
- 对请求:所有人都必须经过安检门,安检门可以检查你是否带了危险品(非法请求),或者帮你整理一下衣冠(修改请求头)。
- 对响应:所有离开大楼的包裹都要经过检查,可以检查是否夹带了不该带出去的东西(敏感信息),或者给包裹贴上一个统一的标签(添加响应头)。
过滤器的主要作用
过滤器主要用于实现横切关注点,即那些与核心业务逻辑无关但又需要在多个地方被处理的功能,常见的应用场景包括:
- 认证与授权:检查用户是否登录,是否有权限访问某个资源。
- 日志记录:记录所有请求的详细信息,如 IP 地址、访问时间、请求参数等,用于监控和审计。
- 数据压缩:对响应数据进行压缩(如 GZIP),以减少网络传输量,加快页面加载速度。
- 字符编码转换:统一处理请求和响应的字符编码,解决中文乱码问题(非常经典和实用的场景)。
- 输入验证与净化:对用户输入的数据进行验证(如检查格式)和净化(如防止 XSS 攻击)。
- 敏感信息过滤:从响应中过滤掉敏感数据(如身份证号、手机号)。
- 资源访问控制:限制某些 IP 地址的访问。
过滤器的工作原理
过滤器的工作依赖于一个名为 Filter Chain(过滤器链)的机制。
- 当一个请求匹配到某个 URL 模式时,Web 容器(如 Tomcat)会查找所有配置了该模式的过滤器。
- 这些过滤器会按照它们在
web.xml中声明的顺序(或使用@WebFilter注解时类加载的顺序)组成一个过滤器链。 - 请求会沿着这个链依次传递:
- 请求到达 Filter 1。
- Filter 1 执行其
doFilter()方法,在这个方法的开头,它会调用chain.doFilter(request, response),这个调用就像一个“开关”,它会将请求传递给链中的下一个组件(可能是 Filter 2,也可能是最终的 Servlet)。 - 当 Servlet 处理完请求并生成响应后,响应会沿着过滤器链反向返回。
- 响应回到 Filter 2 的
doFilter()方法中,在chain.doFilter()调用之后的代码。 - 然后响应回到 Filter 1 的
doFilter()方法中,在chain.doFilter()调用之后的代码。 - 响应被发送回客户端。
关键点:chain.doFilter() 之前的代码在请求进入目标资源之前执行,chain.doFilter() 之后的代码在响应离开目标资源之后执行。

如何创建和配置一个过滤器?
有两种主要的方式来配置过滤器:传统的 web.xml 方式和现代的注解方式。
使用 @WebFilter 注解(推荐)
这是现代 Servlet (3.0+) 推荐的方式,更加简洁。
创建过滤器类
创建一个类,实现 javax.servlet.Filter 接口。

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// @WebFilter 注解用于指定此过滤器要拦截哪些 URL
// "/*" 表示拦截所有请求
@WebFilter("/*")
public class LoggingFilter implements Filter {
// 初始化方法,Web 容器启动时调用,只调用一次
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("LoggingFilter 初始化...");
}
// 核心过滤方法,每次请求匹配时都会调用
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将请求和响应对象转换为 HttpServletRequest 和 HttpServletResponse
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// --- 请求处理 ---
System.out.println("请求到达: " + req.getRequestURI() + " 来自: " + req.getRemoteAddr());
// 将请求传递给下一个过滤器或目标 Servlet
// 这是过滤器链的核心!
chain.doFilter(request, response);
// --- 响应处理 ---
System.out.println("响应离开: " + req.getRequestURI());
}
// 销毁方法,Web 容器关闭时调用,只调用一次
@Override
public void destroy() {
System.out.println("LoggingFilter 销毁...");
}
}
部署
确保你的项目是 Servlet 3.0+ 版本,并且你的 Web 容器(如 Tomcat 7+)支持,将编译后的 .class 文件放在 WEB-INF/classes 目录下即可,容器会自动扫描并加载带有 @WebFilter 注解的类。
使用 web.xml 部署描述符(传统方式)
创建过滤器类
实现 Filter 接口,但不需要加任何注解。
// 这个类和上面注解方式的类一样,只是没有 @WebFilter 注解
public class LoggingFilter implements Filter {
// ... init, doFilter, destroy 方法实现同上 ...
}
配置 web.xml
在 WEB-INF/web.xml 文件中添加以下配置:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 1. 定义过滤器 -->
<filter>
<!-- 过滤器的唯一名称 -->
<filter-name>loggingFilter</filter-name>
<!-- 过滤器的实现类全限定名 -->
<filter-class>com.example.LoggingFilter</filter-class>
</filter>
<!-- 2. 将过滤器映射到 URL 模式 -->
<filter-mapping>
<!-- 引用上面定义的过滤器名称 -->
<filter-name>loggingFilter</filter-name>
<!-- 指定要拦截的 URL 模式 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
实战案例:字符编码过滤器
这是一个非常经典且实用的过滤器,用于解决 POST 请求和响应的中文乱码问题。
编码过滤器类 CharacterEncodingFilter.java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 可以从 web.xml 中读取配置的编码
String encodingParam = filterConfig.getInitParameter("encoding");
if (encodingParam != null) {
this.encoding = encodingParam;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置请求的字符编码
request.setCharacterEncoding(encoding);
// 设置响应的字符编码
response.setCharacterEncoding(encoding);
// 也可以设置响应头,更通用
// response.setContentType("text/html;charset=" + encoding);
// 将请求和响应传递下去
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 清理资源
}
}
在 web.xml 中配置(可选)
如果你想通过 web.xml 来配置编码,可以这样写:
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>com.example.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器链的顺序
当有多个过滤器时,它们的执行顺序非常重要。
- 使用
@WebFilter:顺序通常由容器的类加载机制决定,不太可靠,为了精确控制顺序,可以给过滤器类添加@Order注解(如果项目使用了 Spring 框架)或者使用javax.annotation.Priority注解,但在纯 Servlet 环境中,推荐使用web.xml。 - 使用
web.xml:顺序是严格按照<filter-mapping>在web.xml中出现的顺序来执行的,先定义的先执行。
<!-- 这个过滤器会先执行 -->
<filter-mapping>
<filter-name>filterA</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 这个过滤器会后执行 -->
<filter-mapping>
<filter-name>filterB</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
请求流程:Client -> Filter A -> Filter B -> Servlet -> Filter B -> Filter A -> Client
| 特性 | 描述 |
|---|---|
| 核心接口 | javax.servlet.Filter |
| 核心方法 | doFilter(ServletRequest, ServletResponse, FilterChain) |
| 核心机制 | chain.doFilter() 调用,将控制权传递给下一个组件。 |
| 配置方式 | 注解 @WebFilter("url-pattern") (推荐,简洁)XML web.xml 中的 <filter> 和 <filter-mapping> (传统,可控性强) |
| 生命周期 | init() -> doFilter() (多次) -> destroy() |
| 主要用途 | 认证、授权、日志、编码、压缩、数据净化等横切功能。 |
Servlet 过滤器是 Java Web 开发中不可或缺的工具,它提供了一种优雅、可复用的方式来处理与业务逻辑无关的系统级任务,极大地提高了代码的模块化和可维护性。
