OncePerRequestFilter是Spring框架提供的一个过滤器基类,它的核心作用是确保在一次完整的HTTP请求中,过滤器的逻辑只会被执行一次,即使请求在服务器内部经过了多次转发或包含多个资源请求23。
🔍 核心原理
OncePerRequestFilter通过内部机制跟踪当前请求是否已经被处理过:
- 使用HttpServletRequest的getAttribute和setAttribute方法
- 结合ThreadLocal变量来标记请求状态
- 首次请求时执行过滤器逻辑并设置标记,后续直接跳过3
⚡ 与普通Filter的区别
| 特性 | Filter | OncePerRequestFilter |
|---|---|---|
| 执行次数 | 可能多次执行 | 保证仅执行一次 |
| 来源 | Servlet容器规范 | Spring框架提供 |
| 适用场景 | 基础过滤需求 | 需要精确控制过滤次数的场景4 |
💡 为什么需要OncePerRequestFilter?
主要有两个原因:
- 适配不同Web容器:并非所有容器都保证过滤器只执行一次2
- 兼容异步请求:Servlet 3.0+中,异步分发可能导致过滤器重复执行2
🛠️ 使用示例
java
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TimingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// 记录请求开始时间
long startTime = System.currentTimeMillis();
// 继续过滤器链
filterChain.doFilter(request, response);
// 记录请求处理时间
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("Request took " + duration + " ms to process");
}
}
📚 实践建议
在Spring环境下开发过滤器时,推荐继承OncePerRequestFilter而不是直接实现Filter接口2。这样可以避免因容器差异导致的重复执行问题,确保过滤器逻辑的可靠性。
建议:在你的下一个Spring项目中,将自定义过滤器改为继承OncePerRequestFilter,这样可以避免潜在的重复处理问题,提升应用的稳定性。