Tomcat8源码分析-启动流程-MapperListener启动

原创
2019/12/08 10:51
阅读数 721

上一篇:Tomcat8源码分析-启动流程-start方法

MapperListener启动主要完成了将资源(class目录、jar、servlet-mapping、welcome list)添加MappedHost下的ContextVersion的不同Wrappers数组当中,以及将自己设置为该Engine及其下面是所有容器的ContainerListener与LifeCycleListener

完成上面的功能有什么作用喃???可以说太重要了,看到“servlet-mapping”就应该想到肯定是与处理请求的时候相关的,没错,当Tomcat处理请求的时候就会根据path与上面处理形成的Wrappers数组进行匹配得到Servlet,并设置给StandardWrapperValve.StandardWrapper.servlet,最终才能调用到对应Servlet的方法。

看看源码

StandardService部分

   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实例化的时候传的参数有哪些,这些参数就说明了它包含了那些资源

        //将新生成的Context添加到Host的contextList当中。在这里也验证了一个Host可以有多个Context(一个应用)
        mapper.addContextVersion(host.getName(), host, contextPath,
                context.getWebappVersion(), context, welcomeFiles, resources,
                wrappers);

其中的resources就是WebResource,里面就是之前已经解析完成的目录与jar资源

 

再来看看ContextVersion在Debug时构造完成后几个关键属性中的值

这张图看清楚了Tomcat启动之后有哪些Wrapper(也就是Servlet可以使用)

这张图再点进去看可以看到目录与jar相关信息

 

总结

MapperListener真的很重要,完成了非常核心的servlet-mapping的分析与加载,让处理请求的阶段可以找到对照表(4中Wrappers数组)进行匹配,从而能够正确的进行业务逻辑处理。

展开阅读全文
打赏
1
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部