Java Servlet Filter 运行在容器层面,需通过web.xml或@WebFilter注册,核心方法init()、doFilter()、destroy()必须全部实现,其中doFilter()须调用chain.doFilter()放行,否则请求中断。

Java Servlet Filter 不是拦截器,也不等价于 Spring 的 @Component 过滤器;它运行在容器(如 Tomcat)层面,必须通过 web.xml 或 @WebFilter 注册才能生效,否则代码写了也完全不触发。
Filter 接口的三个核心方法怎么用
实现 javax.servlet.Filter 接口时,必须重写 init()、doFilter()、destroy()。其中只有 doFilter() 是业务逻辑入口,其余两个生命周期方法常被误认为可省略 —— 实际上若不显式实现,编译会通过但容器启动可能报 NoClassDefFoundError(尤其在旧版 Tomcat 中)。
关键点:
-
doFilter()必须调用chain.doFilter(request, response)才能放行,漏掉这句会导致后续所有 Servlet / Filter 不执行,页面空白且无错误日志 -
init()中可通过filterConfig.getInitParameter("xxx")读取,这是传参唯一合法方式,不能靠构造函数或静态变量 -
destroy()不保证在 JVM 退出前一定被调用,不适合放资源强释放逻辑(比如未关闭的数据库连接)
@WebFilter 注解注册 vs web.xml 配置的区别
两者功能等价,但行为细节不同:使用 @WebFilter 时,类必须是 top-level class(不能是 private static inner class),且扫描依赖 metadata-complete="false"(默认值)和正确的 web.xml 版本声明(如 version="4.0")。而 web.xml 方式更稳定,尤其在混合使用 Jersey、Spring Boot 嵌入式容器时。
立即学习“Java免费学习笔记(深入)”;
常见陷阱:
-
@WebFilter(urlPatterns = "/*")会匹配所有请求,包括静态资源(/css/app.css),若在 filter 中做了字符编码设置,可能干扰图片/字体响应 -
在/api/* web.xml中不支持通配符结尾以外的形式,比如/api/v1/.*无效,必须用/*+ 手动路径判断 - 多个 filter 的执行顺序由注册顺序决定:
web.xml中靠前的先执行;@WebFilter则按类加载顺序,不可控,需用filterName+dispatcherTypes显式约束
如何安全地修改 request / response 内容
原生 HttpServletRequest 和 HttpServletResponse 是只读包装,直接调用 request.setAttribute() 或 response.getWriter().write() 在 doFilter() 中可能失败(例如已提交响应头后写 body 会抛 IllegalStateException)。正确做法是继承 HttpServletRequestWrapper 和 HttpServletResponseWrapper。
典型场景示例(修改请求体):
public class BodyCachingRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public BodyCachingRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream stream = request.getInputStream();
this.cachedBody = stream.readAllBytes(); // Java 9+
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ByteArrayInputStream(cachedBody).asInputStream();
}
}
注意:
- 不要在 filter 中反复调用
request.getInputStream()或request.getReader(),Servlet 规范明确禁止多次读取原始流 - 若要改写响应内容(如添加 JSON 外壳),必须用
HttpServletResponseWrapper包装并覆写getWriter()/getOutputStream(),否则写入会被丢弃 - 字符编码应在
chain.doFilter()前调用request.setCharacterEncoding("UTF-8"),但对 GET 请求无效(参数编码由容器在解析 URL 时决定)
Filter 的真正难点不在写法,而在它介入的是容器最底层的 I/O 流链路 —— 任何异常都可能中断整个请求生命周期,且调试时看不到栈中 filter 的上下文。线上环境务必加 try-catch 并记录原始 request.getRequestURL(),否则连哪条路径触发的问题都难以定位。










