OncePerRequestFilter的作用
OncePerRequestFilter 的作用
在 Spring 框架中,Filter 默认都继承自 OncePerRequestFilter。为什么要这样设计呢?
顾名思义,OncePerRequestFilter 能够确保在一次请求中只通过一次 Filter,避免重复执行。
核心机制
以下是 OncePerRequestFilter 的核心源码实现:
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
if (!hasAlreadyFilteredAttribute && !this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
this.doFilterInternal(httpRequest, httpResponse, filterChain);
} finally {
request.removeAttribute(alreadyFilteredAttributeName);
}
} else {
filterChain.doFilter(request, response);
}
} else {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
}通过代码可以看出,它通过检查请求属性(alreadyFilteredAttributeName)来判断当前请求是否已经被过滤过。如果未过滤,则设置标记并执行 doFilterInternal;如果已存在标记,则直接跳过,从而保证单次执行。
设计初衷:容器兼容性
通常我们认为,一次请求原本就只会经过过滤器一次,为什么还要特意限定呢?
实际上,这一设计是为了兼容不同的 Web 容器(例如遵循 JSR168 规范的容器)而特意实现的。并不是所有的容器都像我们期望的那样只过滤一次,不同的 Servlet 规范版本,其表现也存在差异。
正如该类的 Javadoc 所述:
/**
* Filter base class that guarantees to be just executed once per request,
* on any servlet container. It provides a {@link #doFilterInternal}
* method with HttpServletRequest and HttpServletResponse arguments.
*
* <p>The {@link #getAlreadyFilteredAttributeName} method determines how
* to identify that a request is already filtered. The default implementation
* is based on the configured name of the concrete filter instance.
*
* @author Juergen Hoeller
* @since 06.12.2003
*/Servlet 规范差异示例
不同的 Servlet 版本在处理转发(forward)和包含(include)时行为不同:
- Servlet 2.3:Filter 会过滤一切请求,包括服务器内部使用
forward转发的请求和<%@ include file="/index.jsp" %>的情况。 - Servlet 2.4:Filter 默认只拦截外部提交的请求,
forward和include等内部转发都不会被过滤。但在某些场景下,我们需要在转发时也用到 Filter。
结论
因此,为了兼容各种不同的运行环境和 Servlet 规范版本,默认让 Filter 继承 OncePerRequestFilter 是一个比较稳妥的选择。
说明:文中提到的 Servlet 2.3/2.4 属于较早的规范版本,现代 Servlet 容器(如 Tomcat 8+/9+ 对应 Servlet 3.0/4.0+)行为已趋于一致,但 Spring 保留此设计仍是为了确保跨容器的一致性与健壮性。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/onceperrequestfilter-de-zuo-yong.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。