• tomcat8.5.57源码阅读笔记4.1


    接着上一篇, 该到StandardContext的 init 和 startInternal 方法了.

    StandardContext#initInternal()

    此方法由 org.apache.catalina.util.LifecycleBase#init() 调用而来.

    //org.apache.catalina.core.StandardContext#initInternal
    protected void initInternal() throws LifecycleException {
        super.initInternal();
    
        // Register the naming resources
        if (namingResources != null) {
            namingResources.init();
        }
    
        // Send j2ee.object.created notification
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.object.created",
                    this.getObjectName(), sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }
    }

    super.initInternal() 调用的是 org.apache.catalina.core.ContainerBase#initInternal(), 里面创建了一个阻塞队列线程池

    StandardContext#startInternal

    protected synchronized void startInternal() throws LifecycleException {
            if(log.isDebugEnabled())
                log.debug("Starting " + getBaseName());
    
            // Send j2ee.state.starting notification
            if (this.getObjectName() != null) {
                Notification notification = new Notification("j2ee.state.starting",
                        this.getObjectName(), sequenceNumber.getAndIncrement());
                broadcaster.sendNotification(notification);
            }
    
            setConfigured(false);
            boolean ok = true;
    
            // Currently this is effectively a NO-OP but needs to be called to
            // ensure the NamingResources follows the correct lifecycle
            if (namingResources != null) {
                namingResources.start();
            }
    
            // Post work directory
            // 生成工作目录, work/Catalina/localhost
            postWorkDirectory();
    
            // Add missing components as necessary
            if (getResources() == null) {   // (1) Required by Loader
                if (log.isDebugEnabled())
                    log.debug("Configuring default Resources");
    
                try {
                    setResources(new StandardRoot(this));
                } catch (IllegalArgumentException e) {
                    log.error(sm.getString("standardContext.resourcesInit"), e);
                    ok = false;
                }
            }
            if (ok) {
                resourcesStart();
            }
    
            if (getLoader() == null) {
                WebappLoader webappLoader = new WebappLoader();
                webappLoader.setDelegate(getDelegate());
                setLoader(webappLoader);
            }
    
            // An explicit cookie processor hasn't been specified; use the default
            if (cookieProcessor == null) {
                cookieProcessor = new Rfc6265CookieProcessor();
            }
    
            // Initialize character set mapper
            // 字符集映射
            getCharsetMapper();
    
            // Validate required extensions
            boolean dependencyCheck = true;
            try {
                dependencyCheck = ExtensionValidator.validateApplication
                    (getResources(), this);
            } catch (IOException ioe) {
                log.error(sm.getString("standardContext.extensionValidationError"), ioe);
                dependencyCheck = false;
            }
    
            if (!dependencyCheck) {
                // do not make application available if dependency check fails
                ok = false;
            }
    
            // Reading the "catalina.useNaming" environment variable
            String useNamingProperty = System.getProperty("catalina.useNaming");
            if ((useNamingProperty != null)
                && (useNamingProperty.equals("false"))) {
                useNaming = false;
            }
    
            if (ok && isUseNaming()) {
                if (getNamingContextListener() == null) {
                    NamingContextListener ncl = new NamingContextListener();
                    ncl.setName(getNamingContextName());
                    ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
                    addLifecycleListener(ncl);
                    setNamingContextListener(ncl);
                }
            }
    
            // Standard container startup
            if (log.isDebugEnabled())
                log.debug("Processing standard container startup");
    
    
            // Binding thread
            ClassLoader oldCCL = bindThread();
    
            try {
                if (ok) {
                    // Start our subordinate components, if any
                    Loader loader = getLoader();
                    if (loader instanceof Lifecycle) {
                        ((Lifecycle) loader).start();
                    }
    
                    // since the loader just started, the webapp classloader is now
                    // created.
                    setClassLoaderProperty("clearReferencesRmiTargets",
                            getClearReferencesRmiTargets());
                    setClassLoaderProperty("clearReferencesStopThreads",
                            getClearReferencesStopThreads());
                    setClassLoaderProperty("clearReferencesStopTimerThreads",
                            getClearReferencesStopTimerThreads());
                    setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
                            getClearReferencesHttpClientKeepAliveThread());
                    setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
                            getClearReferencesObjectStreamClassCaches());
                    setClassLoaderProperty("clearReferencesThreadLocals",
                            getClearReferencesThreadLocals());
    
                    // By calling unbindThread and bindThread in a row, we setup the
                    // current Thread CCL to be the webapp classloader
                    unbindThread(oldCCL);
                    oldCCL = bindThread();
    
                    // Initialize logger again. Other components might have used it
                    // too early, so it should be reset.
                    logger = null;
                    getLogger();
    
                    Realm realm = getRealmInternal();
                    if(null != realm) {
                        if (realm instanceof Lifecycle) {
                            ((Lifecycle) realm).start();
                        }
    
                        // Place the CredentialHandler into the ServletContext so
                        // applications can have access to it. Wrap it in a "safe"
                        // handler so application's can't modify it.
                        CredentialHandler safeHandler = new CredentialHandler() {
                            @Override
                            public boolean matches(String inputCredentials, String storedCredentials) {
                                return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
                            }
    
                            @Override
                            public String mutate(String inputCredentials) {
                                return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
                            }
                        };
                        context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
                    }
    
                    // Notify our interested LifecycleListeners
                    // 这里会激发监听器 ContextConfig
                    fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
    
                    // Start our child containers, if not already started 如果Wrapper没启动, 这里会再次尝试启动
                    for (Container child : findChildren()) {
                        if (!child.getState().isAvailable()) {
                            child.start();
                        }
                    }
    
                    // Start the Valves in our pipeline (including the basic),
                    // if any
                    if (pipeline instanceof Lifecycle) {
                        ((Lifecycle) pipeline).start();
                    }
    
                    ......
        }

    2. 这个方法里面会调用 loadOnStartup(findChildren()) 方法, 当 loadOnStartup >=0 时, 会对wrapper进行load操作, 最终执行 servlet#init() 方法.

    1. fireLifecycleEvent 会激发 ContextConfig 的监听方法: org.apache.catalina.startup.ContextConfig#lifecycleEvent

    套路和 HostConfig 是一样的.

    public void lifecycleEvent(LifecycleEvent event) {
    
        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
            return;
        }
    
        // 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();
        }
    }

    根据传入事件, 会进入 configureStart() 方法.

    protected synchronized void configureStart() {
        // Called from StandardContext.start()
    
        if (log.isDebugEnabled()) {
            log.debug(sm.getString("contextConfig.start"));
        }
    
        if (log.isDebugEnabled()) {
            log.debug(sm.getString("contextConfig.xmlSettings",
                    context.getName(),
                    Boolean.valueOf(context.getXmlValidation()),
                    Boolean.valueOf(context.getXmlNamespaceAware())));
        }
    
        //解析 web.xml
        webConfig();
    
        //后加代码, 为了解决报错问题
        context.addServletContainerInitializer(new JasperInitializer(), null);
    
        if (!context.getIgnoreAnnotations()) {
            applicationAnnotationsConfig();
        }
        if (ok) {
            validateSecurityRoles();
        }
    
        // Configure an authenticator if we need one
        if (ok) {
            authenticatorConfig();
        }
    
        // Dump the contents of this pipeline if requested
        if (log.isDebugEnabled()) {
            log.debug("Pipeline Configuration:");
            Pipeline pipeline = context.getPipeline();
            Valve valves[] = null;
            if (pipeline != null) {
                valves = pipeline.getValves();
            }
            if (valves != null) {
                for (Valve valve : valves) {
                    log.debug("  " + valve.getClass().getName());
                }
            }
            log.debug("======================");
        }
    
        // Make our application available if no problems were encountered
        if (ok) {
            context.setConfigured(true);
        } else {
            log.error(sm.getString("contextConfig.unavailable"));
            context.setConfigured(false);
        }
    
    }

    webConfig()方法, 会去解析 web.xml 文件. servlet3.0之前, 都是将 servlet 配置在web.xml文件中, servlet3.0之后, 出现了注解的方式.

    protected void webConfig() {
        //web.xml解析器
        WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                context.getXmlValidation(), context.getXmlBlockExternal());
    
        Set<WebXml> defaults = new HashSet<>();
        defaults.add(getDefaultWebXmlFragment(webXmlParser));
    
        //web.xml解析结果, 放到 WebXml 对象中
        WebXml webXml = createWebXml();
    
        // Parse context level web.xml
        InputSource contextWebXml = getContextWebXmlSource();
        if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
            ok = false;
        }
    
        ServletContext sContext = context.getServletContext();
    
        // Ordering is important here
    
        // Step 1. Identify all the JARs packaged with the application and those
        // provided by the container. If any of the application JARs have a
        // web-fragment.xml it will be parsed at this point. web-fragment.xml
        // files are ignored for container provided JARs.
        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
    
        // Step 2. Order the fragments.
        Set<WebXml> orderedFragments = null;
        orderedFragments = WebXml.orderWebFragments(webXml, fragments, sContext);
    
        // Step 3. Look for ServletContainerInitializer implementations
        if (ok) {
            processServletContainerInitializers();
        }
    
        if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
            // Steps 4 & 5.
            processClasses(webXml, orderedFragments);
        }
    
        if (!webXml.isMetadataComplete()) {
            ......
        } else {
            webXml.merge(defaults);
            convertJsps(webXml);
            //配置方法, 会调用 context.addChild()方法
            configureContext(webXml);
        }
    
        ......
    }

    进 configureContext() 方法看

    private void configureContext(WebXml webxml) {
        // As far as possible, process in alphabetical order so it is easy to
        // check everything is present
        // Some validation depends on correct public ID
        context.setPublicId(webxml.getPublicId());
    
        // Everything else in order
        context.setEffectiveMajorVersion(webxml.getMajorVersion());
        context.setEffectiveMinorVersion(webxml.getMinorVersion());
    
        for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
            context.addParameter(entry.getKey(), entry.getValue());
        }
        context.setDenyUncoveredHttpMethods(
                webxml.getDenyUncoveredHttpMethods());
        context.setDisplayName(webxml.getDisplayName());
        context.setDistributable(webxml.isDistributable());
        for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) {
            context.getNamingResources().addLocalEjb(ejbLocalRef);
        }
        for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
            context.getNamingResources().addEjb(ejbRef);
        }
        for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
            context.getNamingResources().addEnvironment(environment);
        }
        for (ErrorPage errorPage : webxml.getErrorPages().values()) {
            context.addErrorPage(errorPage);
        }
        for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }
        for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }
        context.setJspConfigDescriptor(webxml.getJspConfigDescriptor());
        for (String listener : webxml.getListeners()) {
            context.addApplicationListener(listener);
        }
        for (Entry<String, String> entry :
                webxml.getLocaleEncodingMappings().entrySet()) {
            context.addLocaleEncodingMappingParameter(entry.getKey(),
                    entry.getValue());
        }
        // Prevents IAE
        if (webxml.getLoginConfig() != null) {
            context.setLoginConfig(webxml.getLoginConfig());
        }
        for (MessageDestinationRef mdr :
                webxml.getMessageDestinationRefs().values()) {
            context.getNamingResources().addMessageDestinationRef(mdr);
        }
    
        // messageDestinations were ignored in Tomcat 6, so ignore here
    
        context.setIgnoreAnnotations(webxml.isMetadataComplete());
        for (Entry<String, String> entry :
                webxml.getMimeMappings().entrySet()) {
            context.addMimeMapping(entry.getKey(), entry.getValue());
        }
        // Name is just used for ordering
        for (ContextResourceEnvRef resource :
                webxml.getResourceEnvRefs().values()) {
            context.getNamingResources().addResourceEnvRef(resource);
        }
        for (ContextResource resource : webxml.getResourceRefs().values()) {
            context.getNamingResources().addResource(resource);
        }
        boolean allAuthenticatedUsersIsAppRole =
                webxml.getSecurityRoles().contains(
                        SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS);
        for (SecurityConstraint constraint : webxml.getSecurityConstraints()) {
            if (allAuthenticatedUsersIsAppRole) {
                constraint.treatAllAuthenticatedUsersAsApplicationRole();
            }
            context.addConstraint(constraint);
        }
        for (String role : webxml.getSecurityRoles()) {
            context.addSecurityRole(role);
        }
        for (ContextService service : webxml.getServiceRefs().values()) {
            context.getNamingResources().addService(service);
        }
        for (ServletDef servlet : webxml.getServlets().values()) {
            Wrapper wrapper = context.createWrapper();
            // Description is ignored
            // Display name is ignored
            // Icons are ignored
    
            // jsp-file gets passed to the JSP Servlet as an init-param
    
            if (servlet.getLoadOnStartup() != null) {
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
            }
            if (servlet.getEnabled() != null) {
                wrapper.setEnabled(servlet.getEnabled().booleanValue());
            }
            wrapper.setName(servlet.getServletName());
            Map<String,String> params = servlet.getParameterMap();
            for (Entry<String, String> entry : params.entrySet()) {
                wrapper.addInitParameter(entry.getKey(), entry.getValue());
            }
            wrapper.setRunAs(servlet.getRunAs());
            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
            for (SecurityRoleRef roleRef : roleRefs) {
                wrapper.addSecurityReference(
                        roleRef.getName(), roleRef.getLink());
            }
            wrapper.setServletClass(servlet.getServletClass());
            MultipartDef multipartdef = servlet.getMultipartDef();
            if (multipartdef != null) {
                long maxFileSize = -1;
                long maxRequestSize = -1;
                int fileSizeThreshold = 0;
    
                if(null != multipartdef.getMaxFileSize()) {
                    maxFileSize = Long.parseLong(multipartdef.getMaxFileSize());
                }
                if(null != multipartdef.getMaxRequestSize()) {
                    maxRequestSize = Long.parseLong(multipartdef.getMaxRequestSize());
                }
                if(null != multipartdef.getFileSizeThreshold()) {
                    fileSizeThreshold = Integer.parseInt(multipartdef.getFileSizeThreshold());
                }
    
                wrapper.setMultipartConfigElement(new MultipartConfigElement(
                        multipartdef.getLocation(),
                        maxFileSize,
                        maxRequestSize,
                        fileSizeThreshold));
            }
            if (servlet.getAsyncSupported() != null) {
                wrapper.setAsyncSupported(
                        servlet.getAsyncSupported().booleanValue());
            }
            wrapper.setOverridable(servlet.isOverridable());
            //加入节点Servlet StandardContext#addChild(StandardWrapper)
            context.addChild(wrapper);
        }
        for (Entry<String, String> entry :
                webxml.getServletMappings().entrySet()) {
            context.addServletMappingDecoded(entry.getKey(), entry.getValue());
        }
        SessionConfig sessionConfig = webxml.getSessionConfig();
        if (sessionConfig != null) {
            if (sessionConfig.getSessionTimeout() != null) {
                context.setSessionTimeout(
                        sessionConfig.getSessionTimeout().intValue());
            }
            SessionCookieConfig scc =
                context.getServletContext().getSessionCookieConfig();
            scc.setName(sessionConfig.getCookieName());
            scc.setDomain(sessionConfig.getCookieDomain());
            scc.setPath(sessionConfig.getCookiePath());
            scc.setComment(sessionConfig.getCookieComment());
            if (sessionConfig.getCookieHttpOnly() != null) {
                scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());
            }
            if (sessionConfig.getCookieSecure() != null) {
                scc.setSecure(sessionConfig.getCookieSecure().booleanValue());
            }
            if (sessionConfig.getCookieMaxAge() != null) {
                scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());
            }
            if (sessionConfig.getSessionTrackingModes().size() > 0) {
                context.getServletContext().setSessionTrackingModes(
                        sessionConfig.getSessionTrackingModes());
            }
        }
    
        // Context doesn't use version directly
    
        for (String welcomeFile : webxml.getWelcomeFiles()) {
            /*
             * The following will result in a welcome file of "" so don't add
             * that to the context
             * <welcome-file-list>
             *   <welcome-file/>
             * </welcome-file-list>
             */
            if (welcomeFile != null && welcomeFile.length() > 0) {
                context.addWelcomeFile(welcomeFile);
            }
        }
    
        // Do this last as it depends on servlets
        for (JspPropertyGroup jspPropertyGroup :
                webxml.getJspPropertyGroups()) {
            String jspServletName = context.findServletMapping("*.jsp");
            if (jspServletName == null) {
                jspServletName = "jsp";
            }
            if (context.findChild(jspServletName) != null) {
                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                    context.addServletMappingDecoded(urlPattern, jspServletName, true);
                }
            } else {
                if(log.isDebugEnabled()) {
                    for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                        log.debug("Skipping " + urlPattern + " , no servlet " +
                                jspServletName);
                    }
                }
            }
        }
    
        for (Entry<String, String> entry :
                webxml.getPostConstructMethods().entrySet()) {
            context.addPostConstructMethod(entry.getKey(), entry.getValue());
        }
    
        for (Entry<String, String> entry :
            webxml.getPreDestroyMethods().entrySet()) {
            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
        }
    }

    1. 对 Context 设置了一些属性, 如过滤器, 错误页等

    2. 创建 Wrapper , 并设置一些属性, 如 LoadOnStartup

    3. 通过 context.addChild(wrapper), 将 servlet 加入到 context 的子节点上.

    StandardContext.addChild()

    public void addChild(Container child) {
    
        // Global JspServlet
        Wrapper oldJspServlet = null;
    
        if (!(child instanceof Wrapper)) {
            throw new IllegalArgumentException
                (sm.getString("standardContext.notWrapper"));
        }
    
        boolean isJspServlet = "jsp".equals(child.getName());
    
        // Allow webapp to override JspServlet inherited from global web.xml.
        if (isJspServlet) {
            oldJspServlet = (Wrapper) findChild("jsp");
            if (oldJspServlet != null) {
                removeChild(oldJspServlet);
            }
        }
    
        super.addChild(child);
    
        if (isJspServlet && oldJspServlet != null) {
            /*
             * The webapp-specific JspServlet inherits all the mappings
             * specified in the global web.xml, and may add additional ones.
             */
            String[] jspMappings = oldJspServlet.findMappings();
            for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
                addServletMappingDecoded(jspMappings[i], child.getName());
            }
        }
    }

    这里的 super.addChild(child) 和 StandardHost里面, 调用的 super.addChild() 是一样的.

    @Override
    public void addChild(Container child) {
        if (Globals.IS_SECURITY_ENABLED) {
            PrivilegedAction<Void> dp =
                new PrivilegedAddChild(child);
            AccessController.doPrivileged(dp);
        } else {
            addChildInternal(child);
        }
    }
    
    
    //在解析 Server.xml 文件的时候, 通过 StandardEngine.addChild() 一路调用过来
    //<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true" startStopThreads="1">
    //此时, 不会调用 start() 方法
    private void addChildInternal(Container child) {
    
        if( log.isDebugEnabled() )
            log.debug("Add child " + child + " " + this);
        synchronized(children) {
            if (children.get(child.getName()) != null)
                throw new IllegalArgumentException("addChild:  Child name '" +
                                                   child.getName() +
                                                   "' is not unique");
            child.setParent(this);  // May throw IAE
            children.put(child.getName(), child);
        }
    
        // Start child
        // Don't do this inside sync block - start can be a slow process and
        // locking the children object can cause problems elsewhere
        try {
            if ((getState().isAvailable() ||
                    LifecycleState.STARTING_PREP.equals(getState())) &&
                    startChildren) {
                //addChild的时候, 调用启动方法, 解析Server.xml时, 不会进此方法
                //如果子类没有重写start方法, 则会调用 LifecycleBase#start()
                //StandardHost的时候, 会进此方法, 调用的是 StandardContext.start() -> StandardContext.startInternal()
                //StandardContext的时候, 会进此方法, 调用的是 StandardWrapper.start() -> StandardWrapper.startInternal()
                child.start();
            }
        } catch (LifecycleException e) {
            log.error("ContainerBase.addChild: start: ", e);
            throw new IllegalStateException("ContainerBase.addChild: start: " + e);
        } finally {
            fireContainerEvent(ADD_CHILD_EVENT, child);
        }
    }

     由于 StandardWrapper 没有重写 start() 方法, 所以会调用 LifecycleBase#start()

    public final synchronized void start() throws LifecycleException {
    
        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {
    
            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            }
    
            return;
        }
    
        if (state.equals(LifecycleState.NEW)) {
            //results.add(startStopExecutor.submit(new StartChild(child)));
            //异步执行 StandardHost.start() 方法时, 会走这里
            init();
        } else if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }
    
        try {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a 'controlled' failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {
                // Shouldn't be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            handleSubClassException(t, "lifecycleBase.startFail", toString());
        }
    }

    StandardWrapper中, 没有重写 initInternal(), 所以会调用 org.apache.catalina.core.ContainerBase#initInternal(), 创建一个阻塞队列线程池

    所以, 这里只需要看 startInternal() 方法即可

    StandardWrapper#startInternal()

    protected synchronized void startInternal() throws LifecycleException {
    
        // Send j2ee.state.starting notification
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.state.starting",
                                                        this.getObjectName(),
                                                        sequenceNumber++);
            broadcaster.sendNotification(notification);
        }
    
        // Start up this component
        super.startInternal();
    
        setAvailable(0L);
    
        // Send j2ee.state.running notification
        if (this.getObjectName() != null) {
            Notification notification =
                new Notification("j2ee.state.running", this.getObjectName(),
                                sequenceNumber++);
            broadcaster.sendNotification(notification);
        }
    
    }

    StandardWrapper#startInternal() 到这里就完结了, 因为他是Servlet, 是最后一级, 所以也没有子节点了.

  • 相关阅读:
    剑指Offer 从尾到头打印链表
    剑指Offer 斐波那契数列
    剑指Offer 替换空格
    剑指Offer 二维数组中的查找
    Java EE 学习(8):IDEA + maven + spring 搭建 web(4)- 用户管理
    Java EE 学习(7):IDEA + maven + spring 搭建 web(3)- 配置数据库
    Java EE 学习(6):IDEA + maven + spring 搭建 web(2)- 配置 Spring
    Java EE 学习(5):IDEA + maven + spring 搭建 web(1)
    Java EE 学习(4):IDEA + maven 搭建 web(2)
    Java EE 学习(3):IDEA + maven 搭建 web(1)
  • 原文地址:https://www.cnblogs.com/elvinle/p/13528200.html
Copyright © 2020-2023  润新知