javax.servlet.Filter接口,覆盖
doFilter方法,并在
web.xml或类上用
@WebFilter注解注册
在Java Web开发中,过滤器(Filter)是用于拦截HTTP请求/响应的核心组件,可实现权限控制、日志审计、数据加密/解密、字符编码转换等功能,以下是创建和使用Java过滤器的完整指南,包含多种实现方式、最佳实践及典型场景分析。
核心原理与基础结构
1 技术定位
过滤器属于Servlet规范的一部分,遵循javax.servlet.Filter接口规范,其核心功能是在目标资源(如Servlet、JSP)被调用前/后对请求(Request)和响应(Response)进行预处理或后处理。
2 标准实现流程
| 阶段 | 方法名 | 触发时机 | 作用 |
|---|---|---|---|
| 初始化 | init(FilterConfig) |
容器启动时 | 获取初始化参数,建立资源连接 |
| 过滤处理 | doFilter(ServletRequest, ServletResponse, FilterChain) |
每次请求到达时 | 执行核心逻辑,决定是否放行请求 |
| 销毁 | destroy() |
容器关闭时 | 释放占用的资源 |
3 关键注意事项
- 必须调用
FilterChain.doFilter():若不调用此方法,请求将终止在该过滤器,后续处理无法执行。 - 共享状态管理:多线程环境下需注意同步机制,推荐使用
ThreadLocal或原子类。 - 异常处理:建议捕获所有异常并记录日志,防止因单个请求导致整个服务崩溃。
三种主流配置方式详解
1 注解驱动型(Annotated Style)
适用于现代IDE快速开发,通过@WebFilter直接标注在过滤器类上。
示例代码:
import javax.servlet.;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = {"/"}, initParams = {@InitParam(name="encoding", value="UTF-8")})
public class LoggingFilter implements Filter {
private String encoding;
@Override
public void init(FilterConfig config) throws ServletException {
this.encoding = config.getInitParameter("encoding");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 前置处理:设置统一编码
request.setCharacterEncoding(encoding);
response.setCharacterEncoding(encoding);
// 记录访问日志
System.out.println("[" + new Date() + "] " + request.getRemoteAddr());
// 继续传递请求
chain.doFilter(request, response);
}
}
优势:无需修改配置文件,适合小型项目快速迭代。
️ 限制:不支持复杂的匹配规则(如正则表达式)。
2 web.xml声明式(Descriptive Style)
传统但灵活性更高的配置方式,适合复杂路由规则。
部署描述符片段:
<filter>
<filter-name>SecurityCheck</filter-name>
<filter-class>com.example.SecurityFilter</filter-class>
<init-param>
<param-name>blockedIPs</param-name>
<param-value>192.168.1.,10.0.0.</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SecurityCheck</filter-name>
<url-pattern>/admin/</url-pattern>
<dispatcher-types>REQUEST,FORWARD,INCLUDE</dispatcher-types>
</filter-mapping>
高级特性:
<dispatcher-types>控制触发类型(默认仅REQUEST)- 可组合多个
<url-pattern>实现复合路径匹配 - 支持精确优先级排序(通过
<ordering>
3 程序化动态注册(Programmatic Registration)
适用于需要在运行时调整过滤策略的场景。
实现步骤:
- 创建
ServletContextListener监听器
- 在
contextInitialized事件中注册过滤器 public class DynamicFilterRegistrar implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext ctx = event.getServletContext();
DynamicFilter dynamicFilter = new DynamicFilter();
ctx.addFilter("dynamicPath", dynamicFilter)
.addMappingForUrlPatterns(true, true, "/api/v1/");
}
} 适用场景:插件系统、A/B测试环境切换、灰度发布控制。
实战案例解析
1 字符编码统一处理器
解决中文乱码问题的通用方案:
public class CharsetFilter implements Filter {
private static final String ENCODING = "UTF-8";
@Override
public void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
throws IOException, ServletException {
// 强制设置请求/响应编码
req.setCharacterEncoding(ENCODING);
resp.setCharacterEncoding(ENCODING);
resp.setContentType("text/html;charset=" + ENCODING);
chain.doFilter(req, resp);
}
} 关键点:必须在其他过滤器之前执行,且优先于业务逻辑处理。
2 JWT鉴权过滤器
集成Spring Security的典型应用:
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = resolveToken(request);
try {
if (token != null && tokenProvider.validateToken(token)) {
UserDetails userDetails = tokenProvider.getUserDetails(token);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (ExpiredJwtException ex) {
throw new UnauthorizedAccessException("Token expired");
}
filterChain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
// 从Header/Cookie/Param中提取Token的逻辑
}
} 关联技术:OAuth2.0、RBAC权限模型、微服务网关。
性能优化策略
优化方向
实施方案
预期效果
减少对象创建
使用单例模式,将过滤器设为@Singleton
降低GC压力
异步处理
对非关键路径操作采用CompletableFuture+AsyncListener
提升吞吐量
缓存静态数据
将频繁使用的正则表达式、黑白名单存入ConcurrentHashMap
缩短单次请求处理时间
精简依赖库
替换Guava为Apache Commons Lang,减少类加载开销
加快首次启动速度
监控指标暴露
集成Micrometer metrics,暴露http.server.filter.processing_time指标
便于性能瓶颈定位
常见误区与解决方案
1 误区:"我的过滤器不生效"
根本原因:
- 未正确配置
<url-pattern>导致路径不匹配
- 过滤器顺序错误(安全相关过滤器应放在最前面)
- 忘记调用
chain.doFilter()
排查步骤:
- 检查
web.xml中的<filter-mapping>顺序
- 确认URL模式是否覆盖目标路径
- 添加调试日志验证执行流程
2 误区:"修改后的响应头丢失"
深层原因:
- 某些容器(如Tomcat)默认启用
Cache-Control头压缩
- 响应已被提交到客户端后才尝试修改
解决方案:
- 在
doFilter开头立即设置所需头信息
- 禁用响应缓存:
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- 使用
HttpServletResponseWrapper包装响应对象
相关问答FAQs
Q1: 为什么我的过滤器修改了响应内容但没有效果?
A: 可能存在以下原因:① 修改发生在响应已提交之后(应在chain.doFilter()之前完成修改);② 缺少contentType设置导致浏览器未按预期解析;③ 使用了压缩过滤器(Gzip/Deflate),此时需要特殊处理压缩流,建议使用HttpServletResponseWrapper封装响应对象,并在doFilter方法最开始就进行必要修改。
Q2: 如何让过滤器只作用于特定Dispatcher Type?
A: 在web.xml的<filter-mapping>中添加<dispatcher-types>元素,可选值包括:
REQUEST: 直接请求(默认)
FORWARD: forward请求
INCLUDE: include请求
ERROR: 错误跳转请求
ASYNC: 异步请求
示例配置:
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/secure/</url-pattern>
<dispatcher-types>REQUEST,FORWARD,INCLUDE</dispatcher-types>
</filter-mapping
