1. 程式人生 > 實用技巧 >Spring內嵌Tomcat的過濾器鏈的過濾原理

Spring內嵌Tomcat的過濾器鏈的過濾原理

public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);
public void destroy();
}
Filter是個獨立的介面,其中只有3個方法,初始化,過濾和銷燬.
其中init方法是用來初始化過濾器的, 而入參FilterConfig就是初始化過濾器的引數

destroy方法是一個鉤子方法,用於在銷燬過濾器時呼叫,以實現某些清理行為,比如釋放記憶體,控制代碼,關閉執行緒.

doFilter才是要重點關注的方法,即: 幹活的方法
該方法同時接收請求物件和響應物件,說明該過濾器在設計時,既可以在伺服器處理前加工請求,也可以在處理後加工響應,而事實也確實如此.
此外還接收一個過濾器鏈,通過對比當前過濾器的編號和過濾器鏈上過濾器的總數,可以判斷是否要繼續向下傳遞過濾權.
例如:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
  // 執行當前過濾器的主要邏輯:
if (ignore || (request.getCharacterEncoding() == null)) {
String characterEncoding = selectEncoding(request);
if (characterEncoding != null) {
request.setCharacterEncoding(characterEncoding);
}
}

// 將過濾的接力棒傳給過濾器鏈
chain.doFilter(request, response);
}

過濾器鏈的doFilter:
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
  // 判斷是否有安全檢查,如果有,就判斷是否有執行internalDoFilter的許可權,如果沒有就拋異常
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
  // 如果沒有安全檢查,就直接執行internalDoFilter方法
} else {
internalDoFilter(request,response);
}
}
internalDoFilter又是如何執行?
首先判斷當前過濾器是不是最後一個過濾器,如果不是就繼續執行下一個過濾器的過濾操作, 如果是,就執行當前請求的servlet.service()方法.
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {

// 判斷當前過濾器的編號是不是最後一個了,如果是,就從過濾器列表中獲取下一個過濾器,然後判斷是否有安全檢查,如果沒有,就執行下一個過濾器的doFilter過濾邏輯
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase( ilterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}

  // 如果是最後一個過濾器,就執行service方法
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}

if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
總結:
1. N個過濾器的按順序執行是通過過濾器鏈物件的操作實現,通過判斷當前過濾器的編號來判斷過濾器是否全部執行完畢.
2. 如果沒有執行完畢,就執行下一個,否則就執行servlet的service方法