OncePerRequestFilter原理简介

OncePerRequestFilter是Spring Boot里面的一个过滤器抽象类,其同样在Spring Security里面被广泛用到
这个过滤器抽象类通常被用于继承实现并在每次请求时只执行一次过滤,这里面是如何实现的,我们可以通过源码找到答案

public abstract class OncePerRequestFilter extends GenericFilterBean {
        //一个标记,后面会用到
	public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

	@Override
	public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
			throw new ServletException("OncePerRequestFilter just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		//这里获取一个名称,该名称后面会被用于放到request当作key
		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		//检测当前请求是否已经拥有了该标记,如果拥有该标记则代表该过滤器执行过了(后面注释有说明)
		boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;

		if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {

			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		}
		//如果此过滤器已经被执行过则执行如下的逻辑
		else if (hasAlreadyFilteredAttribute) {

			if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
				doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
				return;
			}

			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		}
		//走到这里说明该过滤器没有被执行过
		else {
			// Do invoke this filter...
			// 在当前请求里面设置一个标记,key就是前面拼接的那个变量,value是true,这个标记如果在request存在则在前面会被检测到并改变hasAlreadyFilteredAttribute的值
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
                                // 这个方法是一个抽象方法需要子类去实现具体的过滤逻辑
				doFilterInternal(httpRequest, httpResponse, filterChain);
			}
			finally {
				// Remove the "already filtered" request attribute for this request.
				// 执行完毕之后移除该标记
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}
    //其余代码略
}

以上通过增加标记的方式来实现过滤器只被执行一次

posted @   xuxianshun  阅读(12085)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
历史上的今天:
2019-10-09 springboot后端校验
点击右上角即可分享
微信分享提示