上一篇: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数组)进行匹配,从而可以正确的进行业务逻辑处理。