我們接着上一篇文章的容器處理來講,當postParseRequest方法返回true時,則由容器繼續處理,在service方法中有connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)這一行:
- Connector調用getService()返回StandardService;
- StandardService調用getContainer返回StandardEngine;
- StandardEngine調用getPipeline返回與其關聯的StandardPipeline;
Engine處理請求
我們在前面的文章中講過StandardEngine
的構造函數為自己的Pipeline添加了基本閥StandardEngineValve
,代碼如下:
public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); try { setJvmRoute(System.getProperty("jvmRoute")); } catch(Exception ex) { log.warn(sm.getString("standardEngine.jvmRouteFail")); } }
接下來我們看看StandardEngineValve
的invoke()
方法。該方法主要是選擇合適的Host,然后調用Host中pipeline的第一個Valve的invoke()
方法。
public final void invoke(Request request, Response response) throws IOException, ServletException { // Select the Host to be used for this Request Host host = request.getHost(); if (host == null) { response.sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost", request.getServerName())); return; } if (request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } // Ask this Host to process this request host.getPipeline().getFirst().invoke(request, response); }
host.getPipeline().getFirst().invoke(request, response)
,可以看到 Host 容器先獲取自己的管道,再獲取第一個閥門,我們再看看該閥門的 invoke 方法。
Host處理請求
分析Host的時候,我們從Host的構造函數入手,該方法主要是設置基礎閥門。public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); }
StandardPipeline調用getFirst得到第一個閥去處理請求,由於基本閥是最后一個,所以最后會由基本閥去處理請求。
StandardHost的Pipeline里面一定有 ErrorReportValve 與 StandardHostValve兩個Valve,ErrorReportValve主要是檢測 Http 請求過程中是否出現過什么異常, 有異常的話, 直接拼裝 html 頁面, 輸出到客戶端。
我們看看ErrorReportValve的invoke方法:
public void invoke(Request request, Response response) throws IOException, ServletException { // Perform the request // 1. 先將 請求轉發給下一個 Valve getNext().invoke(request, response); // 2. 這里的 isCommitted 表明, 請求是正常處理結束 if (response.isCommitted()) { return; } // 3. 判斷請求過程中是否有異常發生 Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (request.isAsyncStarted() && ((response.getStatus() < 400 && throwable == null) || request.isAsyncDispatching())) { return; } if (throwable != null) { // The response is an error response.setError(); // Reset the response (if possible) try { // 4. 重置 response 里面的數據(此時 Response 里面可能有些數據) response.reset(); } catch (IllegalStateException e) { // Ignore } // 5. 這就是我們常看到的 500 錯誤碼 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } response.setSuspended(false); try { // 6. 這里就是將 異常的堆棧信息組合成 html 頁面, 輸出到前台 report(request, response, throwable); } catch (Throwable tt) { ExceptionUtils.handleThrowable(tt); } if (request.isAsyncStarted()) { // 7. 若是異步請求的話, 設置對應的 complete (對應的是 異步 Servlet) request.getAsyncContext().complete(); } }
該方法首先執行了下個閥門的 invoke 方法。然后根據返回的Request 屬性設置一些錯誤信息。那么下個閥門是誰呢?其實就是基礎閥門了:StandardHostValve,該閥門的 invoke 的方法是如何實現的呢?
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Select the Context to be used for this Request Context context = request.getContext(); if (context == null) { response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } // Bind the context CL to the current thread if( context.getLoader() != null ) { // Not started - it should check for availability first // This should eventually move to Engine, it's generic. if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction<Void> pa = new PrivilegedSetTccl( context.getLoader().getClassLoader()); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); } } if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } // Don't fire listeners during async processing // If a request init listener throws an exception, the request is // aborted boolean asyncAtStart = request.isAsync(); // An async error page may dispatch to another resource. This flag helps // ensure an infinite error handling loop is not entered boolean errorAtStart = response.isError(); if (asyncAtStart || context.fireRequestInitEvent(request)) { // Ask this Context to process this request try { context.getPipeline().getFirst().invoke(request, response); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); if (errorAtStart) { container.getLogger().error("Exception Processing " + request.getRequestURI(), t); } else { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); } } // If the request was async at the start and an error occurred then // the async error handling will kick-in and that will fire the // request destroyed event *after* the error handling has taken // place if (!(request.isAsync() || (asyncAtStart && request.getAttribute( RequestDispatcher.ERROR_EXCEPTION) != null))) { // Protect against NPEs if context was destroyed during a // long running request. if (context.getState().isAvailable()) { if (!errorAtStart) { // Error page processing response.setSuspended(false); Throwable t = (Throwable) request.getAttribute( RequestDispatcher.ERROR_EXCEPTION); if (t != null) { throwable(request, response, t); } else { status(request, response); } } context.fireRequestDestroyEvent(request); } } } // Access a session (if present) to update last accessed time, based on a // strict interpretation of the specification if (ACCESS_SESSION) { request.getSession(false); } // Restore the context classloader if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction<Void> pa = new PrivilegedSetTccl( StandardHostValve.class.getClassLoader()); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader (StandardHostValve.class.getClassLoader()); } }
Context處理請求
接着Context會去處理請求,同理,StandardContextValve的invoke方法會被調用:
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Select the Wrapper to be used for this Request Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Acknowledge the request try { response.sendAcknowledgement(); } catch (IOException ioe) { container.getLogger().error(sm.getString( "standardContextValve.acknowledgeException"), ioe); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); }
Wrapper處理請求
Wrapper是一個Servlet的包裝,我們先來看看構造方法。主要作用就是設置基礎閥門StandardWrapperValve
。
public StandardWrapper() { super(); swValve=new StandardWrapperValve(); pipeline.setBasic(swValve); broadcaster = new NotificationBroadcasterSupport(); }
接下來我們看看StandardWrapperValve
的invoke()
方法。
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Initialize local variables we may need boolean unavailable = false; Throwable throwable = null; // This should be a Request attribute... long t1=System.currentTimeMillis(); requestCount.incrementAndGet(); StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); // Check for the application being marked unavailable if (!context.getState().isAvailable()) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardContext.isUnavailable")); unavailable = true; } // Check for the servlet being marked unavailable if (!unavailable && wrapper.isUnavailable()) { container.getLogger().info(sm.getString("standardWrapper.isUnavailable", wrapper.getName())); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } unavailable = true; } // Allocate a servlet instance to process this request try { // 關鍵點1:這兒調用Wrapper的allocate()方法分配一個Servlet實例 if (!unavailable) { servlet = wrapper.allocate(); } } catch (UnavailableException e) { container.getLogger().error( sm.getString("standardWrapper.allocateException", wrapper.getName()), e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } } catch (ServletException e) { container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), StandardWrapper.getRootCause(e)); throwable = e; exception(request, response, e); } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; } MessageBytes requestPathMB = request.getRequestPathMB(); DispatcherType dispatcherType = DispatcherType.REQUEST; if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC; request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType); request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB); // Create the filter chain for this request // 關鍵點2,創建過濾器鏈,類似於Pipeline的功能 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method try { if ((servlet != null) && (filterChain != null)) { // Swallow output if needed if (context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { // 關鍵點3,調用過濾器鏈的doFilter,最終會調用到Servlet的service方法 filterChain.doFilter(request.getRequest(), response.getResponse()); } } finally { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { context.getLogger().info(log); } } } else { if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { // 關鍵點3,調用過濾器鏈的doFilter,最終會調用到Servlet的service方法 filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException e) { throwable = e; exception(request, response, e); } catch (IOException e) { container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); } catch (UnavailableException e) { container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); // throwable = e; // exception(request, response, e); wrapper.unavailable(e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } // Do not save exception in 'throwable', because we // do not want to do exception(request, response, e) processing } catch (ServletException e) { Throwable rootCause = StandardWrapper.getRootCause(e); if (!(rootCause instanceof ClientAbortException)) { container.getLogger().error(sm.getString( "standardWrapper.serviceExceptionRoot", wrapper.getName(), context.getName(), e.getMessage()), rootCause); } throwable = e; exception(request, response, e); } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); } // Release the filter chain (if any) for this request // 關鍵點4,釋放掉過濾器鏈及其相關資源 if (filterChain != null) { filterChain.release(); } // 關鍵點5,釋放掉Servlet及相關資源 // Deallocate the allocated servlet instance try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, e); } } // If this servlet has been marked permanently unavailable, // unload it and release this instance // 關鍵點6,如果servlet被標記為永遠不可達,則需要卸載掉它,並釋放這個servlet實例 try { if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, e); } } long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time; }
通過閱讀源碼,我們發現了幾個關鍵點。現羅列如下,后面我們會逐一分析這些關鍵點相關的源碼。
- 關鍵點1:這兒調用Wrapper的allocate()方法分配一個Servlet實例
- 關鍵點2,創建過濾器鏈,類似於Pipeline的功能
- 關鍵點3,調用過濾器鏈的doFilter,最終會調用到Servlet的service方法
- 關鍵點4,釋放掉過濾器鏈及其相關資源
- 關鍵點5,釋放掉Servlet及相關資源
- 關鍵點6,如果servlet被標記為永遠不可達,則需要卸載掉它,並釋放這個servlet實例
關鍵點1 - Wrapper分配Servlet實例
我們來分析一下Wrapper.allocate()方法
@Override public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception // 卸載過程中,不能分配Servlet if (unloading) { throw new ServletException(sm.getString("standardWrapper.unloading", getName())); } boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time // 如果Wrapper沒有實現SingleThreadedModel,則每次都會返回同一個Servlet if (!singleThreadModel) { // Load and initialize our instance if necessary // 實例為null或者實例還未初始化,使用synchronized來保證並發時的原子性 if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) { log.debug("Allocating non-STM instance"); } // Note: We don't know if the Servlet implements // SingleThreadModel until we have loaded it. // 加載Servlet instance = loadServlet(); newInstance = true; if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); } } // 初始化Servlet if (!instanceInitialized) { initServlet(instance); } } } if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } // 非單線程模型,直接返回已經創建的Servlet,也就是說,這種情況下只會創建一個Servlet else { if (log.isTraceEnabled()) { log.trace(" Returning non-STM instance"); } // For new instances, count will have been incremented at the // time of creation if (!newInstance) { countAllocated.incrementAndGet(); } return instance; } } // 如果是單線程模式,則使用servlet對象池技術來加載多個Servlet synchronized (instancePool) { while (countAllocated.get() >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { try { instancePool.push(loadServlet()); nInstances++; } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { // Ignore } } } if (log.isTraceEnabled()) { log.trace(" Returning allocated STM instance"); } countAllocated.incrementAndGet(); return instancePool.pop(); } }
總結下來,注意以下幾點即可:
- 卸載過程中,不能分配Servlet
- 如果不是單線程模式,則每次都會返回同一個Servlet(默認Servlet實現方式)
Servlet
實例為null
或者Servlet
實例還未初始化
,使用synchronized來保證並發時的原子性- 如果是單線程模式,則使用servlet對象池技術來加載多個Servlet
接下來我們看看loadServlet()
方法
public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null)) return instance; PrintStream out = System.out; if (swallowOutput) { SystemLogHandler.startCapture(); } Servlet servlet; try { long t1=System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); } // 關鍵的地方,就是通過實例管理器,創建Servlet實例,而實例管理器是通過特殊的類加載器來加載給定的類 InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { servlet = (Servlet) instanceManager.newInstance(servletClass); } catch (ClassCastException e) { unavailable(null); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.notServlet", servletClass), e); } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); unavailable(null); // Added extra log statement for Bugzilla 36630: // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630 if(log.isDebugEnabled()) { log.debug(sm.getString("standardWrapper.instantiate", servletClass), e); } // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.instantiate", servletClass), e); } if (multipartConfigElement == null) { MultipartConfig annotation = servlet.getClass().getAnnotation(MultipartConfig.class); if (annotation != null) { multipartConfigElement = new MultipartConfigElement(annotation); } } // Special handling for ContainerServlet instances // Note: The InstanceManager checks if the application is permitted // to load ContainerServlets if (servlet instanceof ContainerServlet) { ((ContainerServlet) servlet).setWrapper(this); } classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) { if (instancePool == null) { instancePool = new Stack<>(); } singleThreadModel = true; } // 調用Servlet的init方法 initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } return servlet; }
關鍵的地方有兩個:
- 通過實例管理器,創建Servlet實例,而實例管理器是通過特殊的類加載器來加載給定的類
- 調用Servlet的init方法
關鍵點2 - 創建過濾器鏈
創建過濾器鏈是調用的org.apache.catalina.core.ApplicationFilterFactory
的createFilterChain()
方法。我們來分析一下這個方法。該方法需要注意的地方已經在代碼的comments里面說明了。
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) { // If there is no servlet to execute, return null if (servlet == null) return null; // Create and initialize a filter chain object // 1. 如果加密打開了,則可能會多次調用這個方法 // 2. 為了避免重復生成filterChain對象,所以會將filterChain對象放在Request里面進行緩存 ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; if (Globals.IS_SECURITY_ENABLED) { // Security: Do not recycle filterChain = new ApplicationFilterChain(); } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { // Request dispatcher in use filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported()); // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); // 從這兒看出過濾器鏈對象里面的元素是根據Context里面的filterMaps來生成的 FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR); String requestPath = null; Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR); if (attribute != null){ requestPath = attribute.toString(); } String servletName = wrapper.getName(); // Add the relevant path-mapped filters to this filter chain // 類型和路徑都匹配的情況下,將context.filterConfig放到過濾器鏈里面 for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // Add filters that match on servlet name second // 類型和servlet名稱都匹配的情況下,將context.filterConfig放到過濾器鏈里面 for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // Return the completed filter chain return filterChain; }
關鍵點3 - 調用過濾器鏈的doFilter
ApplicationFilterChain類的doFilter函數代碼如下,它會將處理委托給internalDoFilter函數。
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { 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); } } else { internalDoFilter(request,response); } }
ApplicationFilterChain類的internalDoFilter函數代碼如下:
// 1. `internalDoFilter`方法通過pos和n來調用過濾器鏈里面的每個過濾器。pos表示當前的過濾器下標,n表示總的過濾器數量 // 2. `internalDoFilter`方法最終會調用servlet.service()方法 private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one // 1. 當pos小於n時, 則執行Filter if (pos < n) { // 2. 得到 過濾器 Filter,執行一次post++ ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.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 { // 4. 這里的 filter 的執行 有點遞歸的感覺, 通過 pos 來控制從 filterChain 里面拿出那個 filter 來進行操作 // 這里把this(filterChain)傳到自定義filter里面,我們自定義的filter,會重寫doFilter,在這里會被調用,doFilter里面會執行業務邏輯,如果執行業務邏輯成功,則會調用 filterChain.doFilter(servletRequest, servletResponse); ,filterChain就是這里傳過去的this;如果業務邏輯執行失敗,則return,filterChain終止,后面的servlet.service(request, response)也不會執行了 // 所以在 Filter 里面所調用 return, 則會終止 Filter 的調用, 而下面的 Servlet.service 更本就沒有調用到 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; } // 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 { //當pos等於n時,過濾器都執行完畢,終於執行了熟悉的servlet.service(request, response)方法。 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); } } }
自定義Filter
@WebFilter(urlPatterns = "/*", filterName = "myfilter") public class FileterController implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Filter初始化中"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("登錄邏輯"); if("登錄失敗"){ response.getWriter().write("登錄失敗"); //后面的攔截器和servlet都不會執行了 return; } //登錄成功,執行下一個過濾器 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { System.out.println("Filter銷毀中"); } }
- pos和n是ApplicationFilterChain的成員變量,分別表示過濾器鏈的當前位置和過濾器總數,所以當pos小於n時,會不斷執行ApplicationFilterChain的doFilter方法;
- 當pos等於n時,過濾器都執行完畢,終於執行了熟悉的servlet.service(request, response)方法。