前言
上篇文章中講到了 CoyoteAdapter 的 asyncDispatch 和 service 方法處理請求的過程,其中最重要的就是調用了 Engine 裏的 Pipeline 對象的 Valve 對象的 invoke 方法,也就是 StandardEngineValve#invoke 方法。Valve 對象相關介紹在這篇文章裏提到過,這裏就很少說了。java
1. StandardEngineValve#invokeweb
/** * Select the appropriate child Host to process this request, * based on the requested server name. If no matching Host can * be found, return an appropriate HTTP error. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */ @Override 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) { // HTTP 0.9 or HTTP 1.0 request without a host when no default host // is defined. This is handled by the CoyoteAdapter. return; } if (request.isAsyncSupported()) { request.setAsyncSupported(host.getPipeline().isAsyncSupported()); } // Ask this Host to process this request host.getPipeline().getFirst().invoke(request, response); }
StandardEngineValve#invoke 比較短,邏輯簡單。
先調用 Host host = request.getHost() 獲取一個 Host 對象,獲取不到就直接返回。segmentfault
/** * @return the Host within which this Request is being processed. */ public Host getHost() { return mappingData.host; } /** * Mapping data. */ protected final MappingData mappingData = new MappingData();
request.getHost() 返回的是 Request 裏 MappingData 屬性對象的 host 屬性,這個屬性是在上篇文章提到的 CoyoteAdpater#postParseRequest 方法裏賦值的。postParseRequest 裏調用了 Service 裏的 Mapper 屬性對象的 map 方法來解析請求並將 Host 對象關聯到 Request 的。數組
public class MappingData { public Host host = null; public Context context = null; public int contextSlashCount = 0; public Context[] contexts = null; public Wrapper wrapper = null; public boolean jspWildCard = false; public final MessageBytes contextPath = MessageBytes.newInstance(); public final MessageBytes requestPath = MessageBytes.newInstance(); public final MessageBytes wrapperPath = MessageBytes.newInstance(); public final MessageBytes pathInfo = MessageBytes.newInstance(); public final MessageBytes redirectPath = MessageBytes.newInstance(); // Fields used by ApplicationMapping to implement javax.servlet.http.Mapping public MappingMatch matchType = null; // methods …… }
MappingData 裏保存了處理 Request 須要組件,好比 Host,Context、Wrapper 等,這些屬性的初始化都是在 Mapper#map 裏完成的。session
而後調用 host.getPipeline().getFirst().invoke(request, response),也就是調用 Host 對象裏的 Pipeline 屬性對象裏的 Valve 對象的 invoke 方法,即 StandardHostValve#invoke 方法。app
2. StandardHostValve#invokesocket
/** * Select the appropriate child Context to process this request, * based on the specified request URI. If no matching Context can * be found, return an appropriate HTTP error. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */ @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) { return; } if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); try { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { // Don't fire listeners during async processing (the listener // fired for the request that called startAsync()). // If a request init listener throws an exception, the request // is aborted. return; } // Ask this Context to process this request. Requests that are // already in error must have been routed here to check for // application defined error pages so DO NOT forward them to the the // application for processing. try { if (!response.isErrorReportRequired()) { context.getPipeline().getFirst().invoke(request, response); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); container.getLogger().error("Exception Processing " + request.getRequestURI(), t); // If a new error occurred while trying to report a previous // error allow the original error to be reported. if (!response.isErrorReportRequired()) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); } } // Now that the request/response pair is back under container // control lift the suspension so that the error handling can // complete and/or the container can flush any remaining data response.setSuspended(false); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); // Protect against NPEs if the context was destroyed during a // long running request. if (!context.getState().isAvailable()) { return; } // Look for (and render if found) an application level error page if (response.isErrorReportRequired()) { // If an error has occurred that prevents further I/O, don't waste time // producing an error report that will never be read AtomicBoolean result = new AtomicBoolean(false); response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result); if (result.get()) { if (t != null) { throwable(request, response, t); } else { status(request, response); } } } if (!request.isAsync() && !asyncAtStart) { context.fireRequestDestroyEvent(request.getRequest()); } } finally { // Access a session (if present) to update last accessed time, based // on a strict interpretation of the specification if (ACCESS_SESSION) { request.getSession(false); } context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } }
首先調用了 Context context = request.getContext(),獲取一個 Context 對象,獲取不到就直接返回,這一點跟 StandardEngineValve#invoke 方法第一步相似。jsp
/** * Return the Context within which this Request is being processed. * <p> * This is available as soon as the appropriate Context is identified. * Note that availability of a Context allows <code>getContextPath()</code> * to return a value, and thus enables parsing of the request URI. * * @return the Context mapped with the request */ public Context getContext() { return mappingData.context; } /** * Mapping data. */ protected final MappingData mappingData = new MappingData();
request.getContext() 返回的就是 Request 裏的 MappingData 對象的 context 屬性,這個屬性也是在 CoyoteAdpater#postParseRequest 裏解析並賦值的。async
而後進入try-catch 語句,並在裏面調用了ide
if (!response.isErrorReportRequired()) { context.getPipeline().getFirst().invoke(request, response); }
這一句也跟 StandardEngineValve#invoke 方法裏的相似,就是調用 Context 裏的 Pipeline 屬性對象的 Valve 屬性的 invoke 方法,也就是調用 StandardContextValve#invoke 方法。
再而後就是一些狀態設置、觸發監聽器等收尾工做了。
3. StandardContextValve#invoke
/** * Select the appropriate child Wrapper to process this request, * based on the specified request URI. If no matching Wrapper can * be found, return an appropriate HTTP error. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */ @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); }
首先調用 request.getWrapper() 獲取一個 Wrapper 對象。
/** * @return the Wrapper within which this Request is being processed. */ public Wrapper getWrapper() { return mappingData.wrapper; } /** * Mapping data. */ protected final MappingData mappingData = new MappingData();
這個對象也是從 Request 裏的 MappingData 裏獲取的,它的初始化跟 MappingData 裏的 Host 和 Context 同樣。
而後調用 response.sendAcknowledgement() 方法
最後調用 wrapper.getPipeline().getFirst().invoke(request, response),也就是調用 StandardWrapper#invoke 方法。
整個流程跟 StandardEngineValve#invoke、和 StandardHostValve#invoke 相似,不過中間多了一步調用response.sendAcknowledgement() 方法。response.sendAcknowledgement() 最終會調用 Http11Processor#ack() 方法
@Override protected final void ack() { // Acknowledge request // Send a 100 status back if it makes sense (response not committed // yet, and client specified an expectation for 100-continue) if (!response.isCommitted() && request.hasExpectation()) { inputBuffer.setSwallowInput(true); try { outputBuffer.sendAck(); } catch (IOException e) { setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); } } }
Http11Processor#ack() 也只是簡單地調用 outputBuffer.sendAck(),也就是調用 Http11OutputBuffer#sendAck 方法
public static final byte[] ACK_BYTES = ByteChunk.convertToBytes("HTTP/1.1 100 " + CRLF + CRLF); public void sendAck() throws IOException { if (!response.isCommitted()) { socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length); if (flushBuffer(true)) { throw new IOException(sm.getString("iob.failedwrite.ack")); } } }
sendAck() 就是簡單地將 HTTP/1.1 100 加上回車換行符寫給客戶端。
4. StandardWrapperValve#invoke
/** * Invoke the servlet we are managing, respecting the rules regarding * servlet lifecycle and SingleThreadModel support. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */ @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 { 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 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 { 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 { filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException | CloseNowException e) { if (container.getLogger().isDebugEnabled()) { container.getLogger().debug(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), 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 if (filterChain != null) { filterChain.release(); } // 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 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; }
整段代碼很長,大概分爲5段
一、第一個 try-catch 以前
二、第一個 try-catch
三、第一個 try-catch 到第二個 try-catch 之間
四、第二個 try-catch
五、第二個 try-catch 以後
首先看 1,這一段代碼很簡單就是作一些檢查工做。
再來看 2,第一個 try-catch。在 try 語句裏調用了
wrapper.allocate()
來獲取一個對象。
接着看 3,第一個 try-catch 以後先設置了 request 對象的兩個屬性,而後執行了
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet)
來獲取一個 ApplicationFilterChain 對象。
再接着看 4,獲取到 ApplicationFilterChain 對象後就開始調用
request.getAsyncContextInternal().doInternalDispatch() 或者 filterChain.doFilter(request.getRequest(), response.getResponse())
具體調用哪個方法是由request.isAsyncDispatching() 的值決定的,多數狀況下是調用 filterChain.doFilter 方法。
最後看 5,調用完 filterChain.doFilter 方法以後就是一些後置處理了,好比資源回收。
在整個方法中,最後關鍵的就是調用 ApplicationFilterChain#doFilter 方法來處理了,這個方法將在下篇文章中分析,這裏就很少講了。
4.1 StandardWrapper#allocate
/** * Stack containing the STM instances. */ protected Stack<Servlet> instancePool = null; /** * Allocate an initialized instance of this Servlet that is ready to have * its <code>service()</code> method called. If the servlet class does * not implement <code>SingleThreadModel</code>, the (only) initialized * instance may be returned immediately. If the servlet class implements * <code>SingleThreadModel</code>, the Wrapper implementation must ensure * that this instance is not allocated again until it is deallocated by a * call to <code>deallocate()</code>. * * @exception ServletException if the servlet init() method threw * an exception * @exception ServletException if a loading error occurs */ @Override public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception if (unloading) { throw new ServletException(sm.getString("standardWrapper.unloading", getName())); } boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary 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. 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); } } 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++; } } } 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; } } 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(); } }
整段代碼主要在 if (!singleThreadModel) 和 synchronized (instancePool) 裏。
/** * Does this servlet implement the SingleThreadModel interface? */ protected volatile boolean singleThreadModel = false;
singleThreadModel 屬性是判斷 Servlet 實現類是否是實現了 SingleThreadModel 接口。
if (!singleThreadModel) 裏的羅就就是若是不是單線程模式的話就每次都返回一樣的對象 instance。
synchronized (instancePool) 裏的邏輯是若是已經分配使用的 Servlet 對象的數量達到了 instancePool 裏的總數 nInstances 的值,但沒有超過容許的 maxInstances 數量,就從新調用 loadServlet 加在一個 Servlet 對象,並把它放入 instancePool 裏,若是超過了 maxInstances 就調用 instancePool.wait() 方法等待有 instancePool 裏有新的空閒的 Servlet 實例。最後調用 instancePool.pop() 方法從 instancePool 取出一個 Servlet實例返回。
4.2 ApplicationFilterFactory#createFilterChain
/** * Construct a FilterChain implementation that will wrap the execution of * the specified servlet instance. * * @param request The servlet request we are processing * @param wrapper The wrapper managing the servlet instance * @param servlet The servlet instance to be wrapped * * @return The configured FilterChain instance or null if none is to be * executed. */ 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 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(); 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 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 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; }
createFilterChain 方法裏,先 new 一個 ApplicationFilterChain 對象,或者調用 req.getFilterChain() 獲取一個。
而後調用
FilterMap filterMaps[] = context.findFilterMaps();
獲取 FilterMap 類型的數組,這個 FilterMap 是
/** * The set of filter mappings for this application, in the order * they were defined in the deployment descriptor with additional mappings * added via the {@link ServletContext} possibly both before and after those * defined in the deployment descriptor. */ private final ContextFilterMaps filterMaps = new ContextFilterMaps(); /** * @return the set of filter mappings for this Context. */ @Override public FilterMap[] findFilterMaps() { return filterMaps.asArray(); }
FilterMap 屬性是 Context 用來保存應用程序裏的 web.xml 文件裏配置的 Filter 的相關信息的,好比名字、做用路徑等。
拿到 FilterMap[] 數組後,就是兩個 for 循環,這兩個 for 循環裏的內容是同樣的。首先分別調用
matchDispatcher(filterMaps[i] ,dispatcher) matchFiltersURL(filterMaps[i], requestPath)
來判斷 FilterMap 關聯的 Filter 的屬性是否能匹配上,若是能匹配上,就調用
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName());
來獲取一個 ApplicationFilterConfig 對象,並調用filterChain.addFilter(filterConfig) 把這個對象加入到 filterChain 裏。
4.2.1 StandardContext#findFilterConfig
/** * The set of filter definitions for this application, keyed by * filter name. */ private Map<String, FilterDef> filterDefs = new HashMap<>(); /** * The set of filter configurations (and associated filter instances) we * have initialized, keyed by filter name. */ private Map<String, ApplicationFilterConfig> filterConfigs = new HashMap<>(); /** * Find and return the initialized <code>FilterConfig</code> for the * specified filter name, if any; otherwise return <code>null</code>. * * @param name Name of the desired filter * @return the filter config object */ public FilterConfig findFilterConfig(String name) { return filterConfigs.get(name); }
filterConfigs 這個 map 是在 StandardContext#filterStart 方法裏放入 ApplicationFilterConfig 對象的。
/** * The set of filter definitions for this application, keyed by * filter name. */ private Map<String, FilterDef> filterDefs = new HashMap<>(); /** * Configure and initialize the set of filters for this Context. * @return <code>true</code> if all filter initialization completed * successfully, or <code>false</code> otherwise. */ public boolean filterStart() { if (getLogger().isDebugEnabled()) { getLogger().debug("Starting filters"); } // Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterConfigs) { filterConfigs.clear(); for (Entry<String,FilterDef> entry : filterDefs.entrySet()) { String name = entry.getKey(); if (getLogger().isDebugEnabled()) { getLogger().debug(" Starting filter '" + name + "'"); } try { ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); getLogger().error(sm.getString( "standardContext.filterStart", name), t); ok = false; } } } return ok; }
filterStart() 方法將 filterDefs 裏定義的 Filter 放入到建立的 ApplicationFilterConfig 對象中,並把 ApplicationFilterConfig 對象存在 filterConfigs 這個 map 裏。filterStart() 是在 Context#startInternal 裏調用的。
在 Context 初始化過程當中,會對 web.xml 裏定義的 Filter 進行解析,並把解析結果放在 filterDefs 這個 map 裏。
小結本文分析了 StandardEngineValve、StandardHostValve、StandardContextValve 和 StandardWrapperValve 的 invoke 方法,這三個 Valve 的 invoke 方法的核心邏輯就是調用子容器的 Pipeline 的 Valve 的invoke 方法,也就是 StandardEngineValve#invoke -> StandardHostValve#invoke -> StandardContextValve#invoke -> StandardWrapper#invoke 方法。而 StandardWrapper#invoke 最終調用 ApplicationFilterChain#doFilter 方法來處理請求。