Closed Alice52 closed 3 years ago
通过增加标记的方式来实现过滤器只被执行一次
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); } } } //其余代码略 }
OncePerRequestFilter
通过增加标记的方式来实现过滤器只被执行一次