tomcat源码解析(二)--web.xml文件的解析

根据上一章所知,tomcat解析server.xml的规则是在org.apache.catalina.startup.Catalina类的createStartDigester方法里面,部分代码如下:

 protected Digester createStartDigester() {
       ......
        digester.addRuleSet(new EngineRuleSet("Server/Service/")); 
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
       ......
    }

看到HostRuleSet类的addRuleInstances方法,部分代码如下:

 @Override
    public void addRuleInstances(Digester digester) {

        digester.addObjectCreate(prefix + "Host",
                                 "org.apache.catalina.core.StandardHost",
                                 "className");
        digester.addSetProperties(prefix + "Host");
        ......
        digester.addRule(prefix + "Host", 
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.HostConfig",
                          "hostConfigClass"));
        ......

    }

从中可以知道这个的host标签创建的是org.apache.catalina.core.StandardHost类的实例,并且由LifecycleListenerRule类可以知道,org.apache.catalina.startup.HostConfig作为StandardHost的一个监听类.
LifecycleListenerRule类的begin方法部分代码如下:

 public void begin(String namespace, String name, Attributes attributes)
        throws Exception {

        Container c = (Container) digester.peek(); // 这个就是StandardHost的实例
        ......
        Class<?> clazz = Class.forName(className);
        LifecycleListener listener =
            (LifecycleListener) clazz.newInstance(); // 实例化HostConfig类

        c.addLifecycleListener(listener); // 添加到standardHost里面去
    }

从server.xml文件可以知道host的父容器是engin,而从org.apache.catalina.startup.Catalina类的createStartDigester可以知道server.xml的engin标签创建的是org.apache.catalina.core.StandardEngine类的实例.从上一章分析可以知道StandardEngine的启动是在StandardService类的startInternal方法里面启动的,部分代码如下:

 @Override
    protected void startInternal() throws LifecycleException {
      ......
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }

        ......

        mapperListener.start();

        ......
    }

这时候看到StandardEngine的start方法,而start方法又调用了startInternal方法,部分代码如下:

 @Override
    protected synchronized void startInternal() throws LifecycleException {
        ......
        super.startInternal();
    }

这时候调用的是父类ContainerBase的startInternal方法,部分代码如下:

  @Override
    protected synchronized void startInternal() throws LifecycleException {

       ......
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }
        ......
        threadStart();
    }

看到代码Container children[] = findChildren();因为之前StandardHost实例化的时候作为StandardEngine的子容器添加到StandardEngine里面的,因此findChildren就可以获取到StandardHost的实例.从StartChild类知道调用了StandardHost的start方法,而StandardHost的start的方法有调用了ContainerBase的startInternal方法,找到该方法里面的threadStart方法,部分代码如下:

protected void threadStart() {
       ......
        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(true);
        thread.start();
        ......
    }

该方法新启动了线程执行的是ContainerBackgroundProcessor里面的run方法,部分代码如下:

   @Override
        public void run() {
         ......
         processChildren(ContainerBase.this);
         ......
        }

看到processChildren方法,部分代码如下:

 protected void processChildren(Container container) {
         ......
         container.backgroundProcess();
         ......
        }

因为这个时候Container 是StandardHost,而该类没有覆盖此方法因此调用的还是ContainerBase的,该方法的部分代码如下:

 @Override
    public void backgroundProcess() {
    ......
    fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
    ......
    }

在这里看到fireLifecycleEvent,在分析LifecycleListenerRule类的begin方法的方法知道HostConfig监听StandardHost的事件,因此这时候看到HostConfig类的

 @Override
    public void lifecycleEvent(LifecycleEvent event) {
        ......
        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) { 
            check(); 
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }

backgroundProcess方法里面出发的事件类型为Lifecycle.PERIODIC_EVENT,因此看到该类的check方法,部分代码如下:

    protected void check() {

        if (host.getAutoDeploy()) {
           ......
            deployApps(); // 检测
        }
    }

再看到deployApps方法,代码如下:

 protected void deployApps() {

        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());  // 
        // Deploy WARs
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);
    }

看到该方法是不是有点熟悉呢,平时我们发布web的时候可以直接拷贝web的目录或者war压缩包到webapps目录下的.因此这里都一一对应了,deployDirectories该方法对应的就是web目录的发布,deployWARs对应的就是war包的发布方式.deployDescriptors的发布方式没有去细看.而在这里我们主要看deployDirectories就好,部分代码如下:

    protected void deployDirectory(ContextName cn, File dir) {
        ......

        try {
            if (deployXML && xml.exists()) {
                synchronized (digesterLock) {
                   ......
                   context = (Context) digester.parse(xml);// 创建StandardContext对象
                   ......
                }          
            } else if (!deployXML && xml.exists()) {
                ......
            } else {
                context = (Context) Class.forName(contextClass).newInstance(); // 创建StandardContext对象
            }

            Class<?> clazz = Class.forName(host.getConfigClass()); // 创建ContextConfig对象
            LifecycleListener listener =
                (LifecycleListener) clazz.newInstance();
            context.addLifecycleListener(listener); 
            context.setName(cn.getName());
            context.setPath(cn.getPath());
            context.setWebappVersion(cn.getVersion());
            context.setDocBase(cn.getBaseName());
            host.addChild(context); 
            ......
           deployed.put(cn.getName(), deployedApp); 
    }

看到digester的创建代码:

protected Digester digester = createDigester(contextClass);

而contextClass的值为:

protected String contextClass = "org.apache.catalina.core.StandardContext";

createDigester方法的代码如下:

protected static Digester createDigester(String contextClassName) {
        Digester digester = new Digester();
        digester.setValidating(false);
        // Add object creation rule
        digester.addObjectCreate("Context", contextClassName, "className");
        // Set the properties on that object (it doesn't matter if extra
        // properties are set)
        digester.addSetProperties("Context");
        return (digester);
    }

由此可知,deployDirectory方法主要是创建StandardContext的实例,并且创建org.apache.catalina.startup.ContextConfig监听StandardContext的事件.又因为StandardContext继承了ContainerBase类,由此可知,肯定会的调用StandardContext的startInternal方法,部分代码如下:

    @Override
    protected synchronized void startInternal() throws LifecycleException { //----在这里启动了
       ......
       fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // 出发解析web.xml的

       ......

       if (ok) {
         if (!loadOnStartup(findChildren())){ // 启动servlet----------启动那些有启动要求的
            ......
         }
       }
      ......
    }

fireLifecycleEvent在这里知道这里会触发ContextConfig的lifecycleEvent方法,部分代码如下:

 @Override
    public void lifecycleEvent(LifecycleEvent event) {

        ......
            context = (Context) event.getLifecycle();
        ......

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            configureStart(); // 开始解析配置了
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart(); // 启动之前
        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
            // Restore docBase for management tools
            if (originalDocBase != null) {
                context.setDocBase(originalDocBase);
            }
        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
            configureStop();// 配置结束开始添加规则
        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init();
        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
            destroy();
        }
    }

在这里知道event.getType()为Lifecycle.CONFIGURE_START_EVENT,configureStart的部分代码如下:

    protected synchronized void configureStart() {
       ......

        webConfig(); // 解析所有的配置

       ......
    }

webConfig方法的部分代码如下:

  protected void webConfig() {
        ......
        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                context.getXmlValidation(), context.getXmlBlockExternal());

        Set<WebXml> defaults = new HashSet<>();
        defaults.add(getDefaultWebXmlFragment(webXmlParser));

        WebXml webXml = createWebXml();

        // Parse context level web.xml
        InputSource contextWebXml = getContextWebXmlSource(); // 读取/WEB-INF/web.xml
        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) { // /WEB-INF/web.xml文件的内容解析到webXml里面
            ok = false;
        }

        ServletContext sContext = context.getServletContext();

        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser); // 解析的是webFragment

        ......

        // Step 3. Look for ServletContainerInitializer implementations
        if (ok) {
            processServletContainerInitializers(); // 这个是解析继承ServletContainerInitializer类的配置,
        }

       ......
    }

从中可以看出:
parseWebXml 是解析web.xml文件的
processJarsForWebFragments 是解析webfragment的
processServletContainerInitializers 是解析继承了ServletContainerInitializer的配置类
下面先分析,parseWebXml方法的部分代码如下:

 public boolean parseWebXml(InputSource source, WebXml dest,
            boolean fragment) {

       ......
        if (fragment) {
            digester = webFragmentDigester;
            ruleSet = webFragmentRuleSet;
        } else {
            digester = webDigester;
            ruleSet = webRuleSet;
        }

        digester.push(dest);
        digester.setErrorHandler(handler);
       ......
            digester.parse(source);

       ......
    }

因为传进来的fragment是false,那么digester = webDigester;看一下webDigester的实现

webDigester = DigesterFactory.newDigester(validation,
                namespaceAware, webRuleSet, blockExternal);

看到webRuleSet的addRuleInstances的方法,部分代码如下:

  @Override
    public void addRuleInstances(Digester digester) {
       ......
        digester.addCallMethod(fullPrefix + "/context-param",
                               "addContextParam", 2); //添加context-param参数两个参数的
        digester.addCallParam(fullPrefix + "/context-param/param-name", 0);
        digester.addCallParam(fullPrefix + "/context-param/param-value", 1);

        digester.addCallMethod(fullPrefix + "/display-name",
                               "setDisplayName", 0);

        digester.addRule(fullPrefix + "/distributable",
                         new SetDistributableRule());

        ......

        digester.addObjectCreate(fullPrefix + "/error-page",
                                 "org.apache.tomcat.util.descriptor.web.ErrorPage");
        digester.addSetNext(fullPrefix + "/error-page", // 添加到父容器
                            "addErrorPage",
                            "org.apache.tomcat.util.descriptor.web.ErrorPage");

        digester.addCallMethod(fullPrefix + "/error-page/error-code",
                               "setErrorCode", 0);
        digester.addCallMethod(fullPrefix + "/error-page/exception-type",
                               "setExceptionType", 0);
        digester.addCallMethod(fullPrefix + "/error-page/location",
                               "setLocation", 0);

        digester.addObjectCreate(fullPrefix + "/filter",
                                 "org.apache.tomcat.util.descriptor.web.FilterDef");
        digester.addSetNext(fullPrefix + "/filter",
                            "addFilter",
                            "org.apache.tomcat.util.descriptor.web.FilterDef");

        digester.addCallMethod(fullPrefix + "/filter/description",
                               "setDescription", 0);
        digester.addCallMethod(fullPrefix + "/filter/display-name",
                               "setDisplayName", 0);
        digester.addCallMethod(fullPrefix + "/filter/filter-class",
                               "setFilterClass", 0);
        digester.addCallMethod(fullPrefix + "/filter/filter-name",
                               "setFilterName", 0);
        digester.addCallMethod(fullPrefix + "/filter/icon/large-icon",
                               "setLargeIcon", 0);
        digester.addCallMethod(fullPrefix + "/filter/icon/small-icon",
                               "setSmallIcon", 0);
        digester.addCallMethod(fullPrefix + "/filter/async-supported",
                "setAsyncSupported", 0);

        digester.addCallMethod(fullPrefix + "/filter/init-param",
                               "addInitParameter", 2);
        digester.addCallParam(fullPrefix + "/filter/init-param/param-name",
                              0);
        digester.addCallParam(fullPrefix + "/filter/init-param/param-value",
                              1);

        digester.addObjectCreate(fullPrefix + "/filter-mapping",
                                 "org.apache.tomcat.util.descriptor.web.FilterMap");
        digester.addSetNext(fullPrefix + "/filter-mapping",
                                 "addFilterMapping",
                                 "org.apache.tomcat.util.descriptor.web.FilterMap");

        digester.addCallMethod(fullPrefix + "/filter-mapping/filter-name",
                               "setFilterName", 0);
        digester.addCallMethod(fullPrefix + "/filter-mapping/servlet-name",
                               "addServletName", 0);
        digester.addCallMethod(fullPrefix + "/filter-mapping/url-pattern",
                               "addURLPattern", 0);

        digester.addCallMethod(fullPrefix + "/filter-mapping/dispatcher",
                               "setDispatcher", 0);

         digester.addCallMethod(fullPrefix + "/listener/listener-class",
                                "addListener", 0);

        digester.addRule(fullPrefix + "/jsp-config",
                         jspConfig);

        digester.addObjectCreate(fullPrefix + "/jsp-config/jsp-property-group",
                                 "org.apache.tomcat.util.descriptor.web.JspPropertyGroup");
        digester.addSetNext(fullPrefix + "/jsp-config/jsp-property-group",
                            "addJspPropertyGroup",
                            "org.apache.tomcat.util.descriptor.web.JspPropertyGroup");
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/deferred-syntax-allowed-as-literal",
                               "setDeferredSyntax", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored",
                               "setElIgnored", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda",
                               "addIncludeCoda", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude",
                               "addIncludePrelude", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/is-xml",
                               "setIsXml", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/page-encoding",
                               "setPageEncoding", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/scripting-invalid",
                               "setScriptingInvalid", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/trim-directive-whitespaces",
                               "setTrimWhitespace", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/url-pattern",
                               "addUrlPattern", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/default-content-type",
                               "setDefaultContentType", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/buffer",
                               "setBuffer", 0);
        digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-undeclared-namespace",
                               "setErrorOnUndeclaredNamespace", 0);

        digester.addRule(fullPrefix + "/login-config",
                         loginConfig);
        ......
        digester.addRule(fullPrefix + "/servlet",
                         new ServletDefCreateRule());
        digester.addSetNext(fullPrefix + "/servlet",
                            "addServlet",
                            "org.apache.tomcat.util.descriptor.web.ServletDef");

        digester.addCallMethod(fullPrefix + "/servlet/init-param",
                               "addInitParameter", 2);
        digester.addCallParam(fullPrefix + "/servlet/init-param/param-name",
                              0);
        digester.addCallParam(fullPrefix + "/servlet/init-param/param-value",
                              1);

        digester.addCallMethod(fullPrefix + "/servlet/jsp-file",
                               "setJspFile", 0);
        digester.addCallMethod(fullPrefix + "/servlet/load-on-startup",
                               "setLoadOnStartup", 0);
        digester.addCallMethod(fullPrefix + "/servlet/run-as/role-name",
                               "setRunAs", 0);

        digester.addObjectCreate(fullPrefix + "/servlet/security-role-ref",
                                 "org.apache.tomcat.util.descriptor.web.SecurityRoleRef");
        digester.addSetNext(fullPrefix + "/servlet/security-role-ref",
                            "addSecurityRoleRef",
                            "org.apache.tomcat.util.descriptor.web.SecurityRoleRef");
        digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-link",
                               "setLink", 0);
        digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-name",
                               "setName", 0);

        digester.addCallMethod(fullPrefix + "/servlet/servlet-class",
                              "setServletClass", 0);
        digester.addCallMethod(fullPrefix + "/servlet/servlet-name",
                              "setServletName", 0);

        digester.addObjectCreate(fullPrefix + "/servlet/multipart-config",
                                 "org.apache.tomcat.util.descriptor.web.MultipartDef");
        digester.addSetNext(fullPrefix + "/servlet/multipart-config",
                            "setMultipartDef",
                            "org.apache.tomcat.util.descriptor.web.MultipartDef");
        digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location",
                               "setLocation", 0);
        digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-file-size",
                               "setMaxFileSize", 0);
        digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-request-size",
                               "setMaxRequestSize", 0);
        digester.addCallMethod(fullPrefix + "/servlet/multipart-config/file-size-threshold",
                               "setFileSizeThreshold", 0);

        digester.addCallMethod(fullPrefix + "/servlet/async-supported",
                               "setAsyncSupported", 0);
        digester.addCallMethod(fullPrefix + "/servlet/enabled",
                               "setEnabled", 0);


        digester.addRule(fullPrefix + "/servlet-mapping",
                               new CallMethodMultiRule("addServletMapping", 2, 0));
        digester.addCallParam(fullPrefix + "/servlet-mapping/servlet-name", 1);
        digester.addRule(fullPrefix + "/servlet-mapping/url-pattern", new CallParamMultiRule(0));

        digester.addRule(fullPrefix + "/session-config", sessionConfig);
        digester.addObjectCreate(fullPrefix + "/session-config",
                                 "org.apache.tomcat.util.descriptor.web.SessionConfig");
        digester.addSetNext(fullPrefix + "/session-config", "setSessionConfig",
                            "org.apache.tomcat.util.descriptor.web.SessionConfig");
        digester.addCallMethod(fullPrefix + "/session-config/session-timeout",
                               "setSessionTimeout", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/name",
                               "setCookieName", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/domain",
                               "setCookieDomain", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/path",
                               "setCookiePath", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/comment",
                               "setCookieComment", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/http-only",
                               "setCookieHttpOnly", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/secure",
                               "setCookieSecure", 0);
        digester.addCallMethod(fullPrefix + "/session-config/cookie-config/max-age",
                               "setCookieMaxAge", 0);
        digester.addCallMethod(fullPrefix + "/session-config/tracking-mode",
                               "addSessionTrackingMode", 0);

        // Taglibs pre Servlet 2.4
        digester.addRule(fullPrefix + "/taglib", new TaglibLocationRule(false));
        digester.addCallMethod(fullPrefix + "/taglib",
                               "addTaglib", 2);
        digester.addCallParam(fullPrefix + "/taglib/taglib-location", 1);
        digester.addCallParam(fullPrefix + "/taglib/taglib-uri", 0);

        // Taglibs Servlet 2.4 onwards
        digester.addRule(fullPrefix + "/jsp-config/taglib", new TaglibLocationRule(true));
        digester.addCallMethod(fullPrefix + "/jsp-config/taglib",
                "addTaglib", 2);
        digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-location", 1);
        digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-uri", 0);

        digester.addCallMethod(fullPrefix + "/welcome-file-list/welcome-file",
                               "addWelcomeFile", 0);

        digester.addCallMethod(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping",
                              "addLocaleEncodingMapping", 2);
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);

        digester.addRule(fullPrefix + "/post-construct",
                new LifecycleCallbackRule("addPostConstructMethods", 2, true));
        digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-class", 0);
        digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-method", 1);

        digester.addRule(fullPrefix + "/pre-destroy",
                new LifecycleCallbackRule("addPreDestroyMethods", 2, false));
        digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-class", 0);
        digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-method", 1);
    }

这边有web.xml的解析信息储存在WebXml类中,接着看到processServletContainerInitializers方法,代码如下:

  protected void processServletContainerInitializers() {

        List<ServletContainerInitializer> detectedScis;
        try {
            WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
            detectedScis = loader.load(ServletContainerInitializer.class);
        } catch (IOException e) {
            log.error(sm.getString(
                    "contextConfig.servletContainerInitializerFail",
                    context.getName()),
                e);
            ok = false;
            return;
        }

        for (ServletContainerInitializer sci : detectedScis) {
            initializerClassMap.put(sci, new HashSet<Class<?>>()); // 为每一个类创建一个容器

            HandlesTypes ht;
            try {
                ht = sci.getClass().getAnnotation(HandlesTypes.class); // 判断有没有HandlesTypes注解
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.info(sm.getString("contextConfig.sci.debug",
                            sci.getClass().getName()),
                            e);
                } else {
                    log.info(sm.getString("contextConfig.sci.info",
                            sci.getClass().getName()));
                }
                continue;
            }
            if (ht == null) {
                continue;
            }
            Class<?>[] types = ht.value(); // 要启动的类有哪些
            if (types == null) {
                continue;
            }

            for (Class<?> type : types) {
                if (type.isAnnotation()) { // 判断有没有注解
                    handlesTypesAnnotations = true;
                } else {
                    handlesTypesNonAnnotations = true;
                }
                Set<ServletContainerInitializer> scis =
                        typeInitializerMap.get(type);
                if (scis == null) {
                    scis = new HashSet<>();
                    typeInitializerMap.put(type, scis);
                }
                scis.add(sci); // 添加进去并把注解弄好的都KO了
            }
        }
    }

用过spring的0配置的话都知道该类的作用,在这里就先不分析该方法的代码了,等后面讲解到javaEE的配置方式再分析.好了到这里web.xml的配置信息基本全部加载完了,加载完之后怎么跟请求联系在一起呢?
在这里我们再看回到StandardService的startInternal方法里面有这么一句:

mapperListener.start();

在这里我们看一下它做了什么看到MapperListener的startInternal方法,部分代码如下:

 @Override
    public void startInternal() throws LifecycleException {

        ......
        Engine engine = service.getContainer();

        ......
        Container[] conHosts = engine.findChildren();
        for (Container conHost : conHosts) {
            Host host = (Host) conHost;
           ......
                registerHost(host);
            ......
        }
    }

从下面的分析知道engine的子容器是standardHost,再看到registerHost方法,部分代码如下:

   private void registerHost(Host host) {

        String[] aliases = host.findAliases();
        mapper.addHost(host.getName(), aliases, host); // 把host添加到mapper里面去了

        for (Container container : host.findChildren()) {
            if (container.getState().isAvailable()) {
                registerContext((Context) container);
            }
        }
        ......
    }

该方法把standardHost添加到mapper里面去了,由上分析可知standardHost的子容器为standardContext.
registerContext方法的部分代码如下:

  private void registerContext(Context context) { // 注册webapp进来,standardContext

        ......
        Host host = (Host)context.getParent();

        WebResourceRoot resources = context.getResources();
        String[] welcomeFiles = context.findWelcomeFiles(); // 知道欢迎页面
        List<WrapperMappingInfo> wrappers = new ArrayList<>();

        for (Container container : context.findChildren()) { // 找到servlet等
            prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);

           ......
        }

        mapper.addContextVersion(host.getName(), host, contextPath,
                context.getWebappVersion(), context, welcomeFiles, resources,
                wrappers);

        ......
    }

根据分析知道standardContext的子容器为standardWraper并且每一个standardWraper对应一个servlet,跟踪代码可知最后standardWraper都储存在了context.wildcardWrappers里面.
好了分析到这里,基本分析完结了.虽然本文很长,代码最后还是要总结一下的.
standardHost代表的是一个虚拟主机
standardContext代表的是webapps目录下的每一个路径
standardWrapper代表的是servlet容器.
好了下一篇接着分析,请求过程了.

评论 3 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

樱天寻

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值