上一篇:Tomcat8源碼分析-啓動流程-start方法java
MapperListener啓動主要完成了將資源(class目錄、jar、servlet-mapping、welcome list)添加MappedHost下的ContextVersion的不一樣Wrappers數組當中,以及將本身設置爲該Engine及其下面是全部容器的ContainerListener與LifeCycleListener設計模式
完成上面的功能有什麼做用喃???能夠說過重要了,看到「servlet-mapping」就應該想到確定是與處理請求的時候相關的,沒錯,當Tomcat處理請求的時候就會根據path與上面處理造成的Wrappers數組進行匹配獲得Servlet,並設置給StandardWrapperValve.StandardWrapper.servlet,最終才能調用到對應Servlet的方法。數組
看看源碼app
StandardService部分jsp
protected void startInternal() throws LifecycleException { 。。。。。省略其餘代碼。。。。。 /** 很重要 * 會完成以下資源的解析與加載 * 1.將Context下的Wrapper(自定義的Servlet\defaultServlet\jspServlet\jspxServlet)進行分析,並最終拆分爲ContextVersion的四個Wrapper數組, * exactWrappers\wildcardWrappers\extensionWrappers\defaultWrapper,他們在Tomcat接收請求時被用做與path匹配,最終得 * 到想要的Servlet * 2.將Context中的WebResource(裏面包含了好比class目錄、jar包添加到ContextVersion * 3.將welcome list配置添加到ContextVersion * */ mapperListener.start(); 。。。。。省略其餘代碼。。。。。 }
調用start方法(按照前面講的模板方法設計模式套路),緊接着會按照Mapper.registerHost-Mapper.registerContext-Mapper.addContextVersion-Mapper.addWrappers-Mapper.-Mapper.addWrapper這個順序進入到下面的源代碼,在這裏面就完成了默認Servlet和自定義Sevlet的Mapping規則加載,等候被使用。源碼分析
protected void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { synchronized (context) { if (path.endsWith("/*")) {//以"/*"開始的path加入到通配符wrapper當中 // Wildcard wrapper String name = path.substring(0, path.length() - 2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.wildcardWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.wildcardWrappers = newWrappers; int slashCount = slashCount(newWrapper.name); if (slashCount > context.nesting) { context.nesting = slashCount; } } } else if (path.startsWith("*.")) {//以"*."開始的path加入到後綴Wrapper當中 // Extension wrapper String name = path.substring(2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.extensionWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.extensionWrappers = newWrappers; } } else if (path.equals("/")) {//設置默認的DefaultMapper // Default wrapper MappedWrapper newWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly); context.defaultWrapper = newWrapper; } else { //到這裏都還沒匹配到的就是精確Wrapper了 // Exact wrapper final String name; if (path.length() == 0) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; } else { name = path; } MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.exactWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.exactWrappers = newWrappers; } } } }
再來看看ContextVersion實例化的時候傳的參數有哪些,這些參數就說明了它包含了那些資源.net
//將新生成的Context添加到Host的contextList當中。在這裏也驗證了一個Host能夠有多個Context(一個應用) mapper.addContextVersion(host.getName(), host, contextPath, context.getWebappVersion(), context, welcomeFiles, resources, wrappers);
其中的resources就是WebResource,裏面就是以前已經解析完成的目錄與jar資源設計
再來看看ContextVersion在Debug時構造完成後幾個關鍵屬性中的值code
這張圖看清楚了Tomcat啓動以後有哪些Wrapper(也就是Servlet能夠使用)blog
這張圖再點進去看能夠看到目錄與jar相關信息
總結
MapperListener真的很重要,完成了很是核心的servlet-mapping的分析與加載,讓處理請求的階段能夠找到對照表(4中Wrappers數組)進行匹配,從而可以正確的進行業務邏輯處理。