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 @ 2020-10-09 17:42  xuxianshun  阅读(12049)  评论(0编辑  收藏  举报