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);
}
}
}
//其余代碼略
}
以上通過增加標記的方式來實現過濾器只被執行一次