什么是 Servlet 过滤器?
Servlet 过滤器 是可以用来拦截客户端请求和服务器响应的对象,它就像一个“安检门”或者“中间人”,位于客户端和目标 Servlet 之间,可以对请求进行预处理,也可以对响应进行后处理。

核心概念:
- 拦截:过滤器可以决定是否将请求传递给下一个资源(如 Servlet、JSP 或另一个过滤器)。
- 链式处理:一个 Web 应用中可以配置多个过滤器,它们会形成一个“过滤器链”(Filter Chain),请求和响应会依次通过这个链上的每一个过滤器。
- 可重用:过滤器是独立于业务逻辑的组件,可以跨多个 Servlet 或 JSP 使用,一个用于字符编码的过滤器可以应用于整个 Web 应用。
(一个简单的请求流程图)
过滤器的主要用途
过滤器非常灵活,用途广泛,常见的场景包括:
- 认证与授权:检查用户是否登录,是否有权限访问某个资源。
- 日志记录:记录所有请求的详细信息,如 IP 地址、访问时间、请求参数等,用于监控和审计。
- 数据压缩:对响应数据进行 GZIP 压缩,减少网络传输量,加快页面加载速度。
- 字符编码转换:统一处理请求和响应的字符编码,解决中文乱码问题(这是最经典的用途之一)。
- 输入数据验证:在请求到达 Servlet 之前,对请求参数进行格式验证(如检查邮箱格式、手机号格式)。
- 图片处理:动态地修改响应中的图片,例如添加水印、缩放等。
- 响应头信息处理:统一添加或修改响应头,如
Cache-Control、X-Frame-Options等,用于增强安全性或缓存控制。
如何创建一个过滤器?
创建一个自定义的过滤器非常简单,只需要遵循以下三步:

步骤 1:创建一个类并实现 Filter 接口
import javax.servlet.*;
import java.io.IOException;
public class MyFirstFilter implements Filter {
// 初始化方法,当容器创建 Filter 实例后调用,只调用一次
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFirstFilter 初始化...");
// 可以从 filterConfig 中获取初始化参数
String param = filterConfig.getInitParameter("paramName");
System.out.println("初始化参数 paramName 的值为: " + param);
}
// 核心过滤方法,每次请求匹配该过滤器时都会调用
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("MyFirstFilter - 开始处理请求");
// --- 1. 前置处理 ---
// 在这里可以对请求进行预处理
// 设置请求编码
request.setCharacterEncoding("UTF-8");
// --- 2. 将请求传递给下一个资源 ---
// chain.doFilter() 是关键!
// 如果不调用这个方法,请求将被拦截,不会到达目标 Servlet。
// 调用后,请求会继续向下传递,通过过滤器链上的下一个过滤器,最终到达目标 Servlet。
// 目标 Servlet 处理完后,响应会再次回到这个 doFilter 方法的这里。
chain.doFilter(request, response);
// --- 3. 后置处理 ---
// 在这里可以对响应进行后处理
// 在响应内容后面追加一段文字
System.out.println("MyFirstFilter - 开始处理响应");
}
// 销毁方法,当容器销毁 Filter 实例前调用,只调用一次
@Override
public void destroy() {
System.out.println("MyFirstFilter 销毁...");
// 在这里进行资源清理工作
}
}
步骤 2:在 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">
<!-- 配置过滤器 -->
<filter>
<!-- 过滤器的唯一名称 -->
<filter-name>MyFirstFilter</filter-name>
<!-- 过滤器的全限定类名 -->
<filter-class>com.example.filter.MyFirstFilter</filter-class>
<!-- 可选:初始化参数 -->
<init-param>
<param-name>paramName</param-name>
<param-value>paramValue</param-value>
</init-param>
</filter>
<!-- 配置过滤器要拦截的 URL 模式 -->
<filter-mapping>
<!-- 指定是哪个过滤器 -->
<filter-name>MyFirstFilter</filter-name>
<!-- 拦截的 URL 模式 -->
<url-pattern>/*</url-pattern> <!-- /* 表示拦截所有请求 -->
<!-- 也可以指定拦截特定的 Servlet -->
<!-- <servlet-name>MyServlet</servlet-name> -->
</filter-mapping>
</web-app>
步骤 3:使用注解方式配置(推荐,Servlet 3.0+)
从 Servlet 3.0 开始,推荐使用注解来配置过滤器,这样更简洁,无需修改 web.xml。
- 在过滤器类上添加
@WebFilter注解:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// @WebFilter 注解用来配置过滤器
// urlPatterns 指定要拦截的 URL 模式
// filterName 指定过滤器的名称,与 web.xml 中的 <filter-name> 对应
// initParams 可以配置初始化参数
@WebFilter(
urlPatterns = {"/*"},
filterName = "MyFirstFilter",
initParams = {
@WebInitParam(name = "paramName", value = "paramValue")
}
)
public class MyFirstFilter implements Filter {
// ... init, doFilter, destroy 方法保持不变 ...
}
- 在
web.xml中(如果存在)添加metadata-complete="false",或者直接删除<web-app>标签,确保容器会扫描注解。web.xml完全不存在,容器默认会扫描。
过滤器链 的工作原理
当一个请求的 URL 匹配了多个 <filter-mapping> 时,这些过滤器就会形成一个过滤器链,它们的执行顺序由 <filter-mapping> 在 web.xml 中声明的顺序决定(注解方式则按类名字典序,或使用 @WebFilter 的 dispatcherTypes 和 dispatcher 等属性控制)。
执行流程:
- 客户端发送一个请求。
- 容器找到第一个过滤器
Filter1,调用其doFilter()方法。 - 在
Filter1的doFilter()方法中,调用chain.doFilter()。 - 容器将请求传递给链上的下一个过滤器
Filter2,调用其doFilter()方法。 - 在
Filter2的doFilter()方法中,调用chain.doFilter()。 - ...这个过程一直持续,直到链上的最后一个过滤器。
- 在最后一个过滤器中调用
chain.doFilter()后,请求终于到达了目标 Servlet。 - 目标 Servlet 执行业务逻辑,生成响应。
- 响应原路返回,再次进入最后一个过滤器的
doFilter()方法中chain.doFilter()的下一行代码(即后置处理部分)。 - 然后返回到上一个过滤器,执行其后置处理部分。
- ...直到响应返回到第一个过滤器,最终发送给客户端。
图示:

客户端
|
|---请求---> [Filter1] --doFilter()--> [Filter2] --doFilter()--> ... --> [Servlet]
| ^ | ^ | ^
| | |后置处理 | |后置处理 | 处理请求
| | v | v | 生成响应
|<---响应----- [Filter1] <--doFilter()-- [Filter2] <--doFilter()-- ... <-- [Servlet]
| | | | |
| | |前置处理 | |前置处理
| | v | v
|<---请求----- [Filter1] --doFilter()--> [Filter2] --doFilter()--> ... --> [Servlet]
|
客户端
实战案例:解决全站中文乱码的过滤器
这是一个非常实用且经典的过滤器。
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 configEncoding = filterConfig.getInitParameter("encoding");
if (configEncoding != null) {
this.encoding = configEncoding;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 1. 设置请求编码
request.setCharacterEncoding(encoding);
// 2. 设置响应编码 (注意:response 的 setCharacterEncoding 必须在 getWriter() 之前调用)
response.setCharacterEncoding(encoding);
// 同时设置 Content-Type 头,更推荐的方式
response.setContentType("text/html;charset=" + encoding);
// 3. 放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 不需要做任何事
}
}
配置 web.xml (可选):
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.example.filter.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>
DispatcherType (分发类型)
从 Servlet 3.0 开始,<filter-mapping> 和 @WebFilter 支持一个 dispatcherTypes 属性,可以指定过滤器在哪些类型的请求分发下生效。
DispatcherType 包括:
REQUEST:默认值,由客户端直接发起的请求。FORWARD:由RequestDispatcher.forward()转发的请求。INCLUDE:由RequestDispatcher.include()包含的请求。ASYNC:异步处理的请求。ERROR:由错误页面机制(<error-page>)触发的请求。
示例:
// 只对直接请求和转发请求生效
@WebFilter(urlPatterns = "/admin/*", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
public class AdminAuthFilter implements Filter {
// ...
}
| 特性 | 描述 |
|---|---|
| 核心作用 | 拦截请求和响应,进行预处理和后处理。 |
| 生命周期 | init() -> doFilter() (多次) -> destroy() |
| 关键方法 | chain.doFilter(request, response):必须调用,用于将控制权传递给下一个资源。 |
| 配置方式 | web.xml (传统) 和 @WebFilter 注解 (现代)。 |
| 核心优势 | 解耦和可重用,将通用的横切关注点(如日志、安全)与业务逻辑分离。 |
| 执行顺序 | 过滤器链的顺序由配置决定。 |
Servlet 过滤器是 Java Web 开发中不可或缺的工具,掌握它对于编写结构清晰、易于维护、功能强大的 Web 应用至关重要。
