Tomcat源碼分析 -- Tomcat的啟動過程(二)

本篇結(jié)構(gòu):

  • 前言
  • 總覽一下main方法
  • init
  • setAwait
  • load
  • start

一立由、前言

上一篇中介紹了startup.bat和catalina.bat腳本。了解到日常雙擊startup.bat啟動tomcat腰根,其實(shí)是來到catalina.bat腳本中按声,由catalina.bat腳本去執(zhí)行org.apache.catalina.startup.Bootstrap這個類中的main方法颁褂。

這里就來看看Bootstrap類的main方法做了些什么厌秒。

二荧呐、總覽一下main方法

public static void main(String args[]) {

    //上部分
    if (daemon == null) {
        // Don't set daemon until init() has completed
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.init();
        } catch (Throwable t) {
            handleThrowable(t);
            t.printStackTrace();
            return;
        }
        daemon = bootstrap;
    } else {
        // When running as a service the call to stop will be on a new
        // thread so make sure the correct class loader is used to prevent
        // a range of class not found exceptions.
        Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
    }

    //下部分
    try {
        String command = "start";
        if (args.length > 0) {
            command = args[args.length - 1];
        }

        if (command.equals("startd")) {
            args[args.length - 1] = "start";
            daemon.load(args);
            daemon.start();
        } else if (command.equals("stopd")) {
            args[args.length - 1] = "stop";
            daemon.stop();
        } else if (command.equals("start")) {
            daemon.setAwait(true);
            daemon.load(args);
            daemon.start();
        } else if (command.equals("stop")) {
            daemon.stopServer(args);
        } else if (command.equals("configtest")) {
            daemon.load(args);
            if (null==daemon.getServer()) {
                System.exit(1);
            }
            System.exit(0);
        } else {
            log.warn("Bootstrap: command \"" + command + "\" does not exist.");
        }
    } catch (Throwable t) {
        // Unwrap the Exception for clearer error reporting
        if (t instanceof InvocationTargetException &&
                t.getCause() != null) {
            t = t.getCause();
        }
        handleThrowable(t);
        t.printStackTrace();
        System.exit(1);
    }

}

可以看出main方法大致可以分為兩部分汉形。

上部分是實(shí)例化一個Bootstrap對象,并調(diào)用init方法倍阐,然后賦值給daemon變量概疆,當(dāng)然如果daemon已經(jīng)不是空了,說明已經(jīng)初始化過了峰搪,就將daemon.catalinaLoader直接設(shè)置到當(dāng)前線程(daemon.catalinaLoader是用來加載tomcat內(nèi)部服務(wù)器所需類的類加載器)岔冀。

下部分是根據(jù)傳遞進(jìn)來的參數(shù)決定走哪一步,當(dāng)雙擊startup.bat時(shí)概耻,傳進(jìn)來的是start使套,所以會來到這段:

else if (command.equals("start")) {
    daemon.setAwait(true);
    daemon.load(args);
    daemon.start();
}

這里主要是調(diào)用三個方法,setAwait鞠柄,load和start侦高。

所以對于Bootstrap重要關(guān)注的就是init,setAwait厌杜,load和start這四個方法奉呛。

三、init

public void init() throws Exception {

    initClassLoaders();

    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

    // Set the shared extensions class loader
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);

    catalinaDaemon = startupInstance;

}

init方法調(diào)用initClassLoaders初始化類加載器夯尽,然后將初始化好的catalinaLoader設(shè)置到當(dāng)前線程瞧壮,接著通過反射調(diào)用org.apache.catalina.startup.Catalina類的setParentClassLoader,將sharedLoader傳入匙握。

3.1咆槽、首先看initClassLoaders方法:

private void initClassLoaders() {
    try {
        commonLoader = createClassLoader("common", null);
        if( commonLoader == null ) {
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader=this.getClass().getClassLoader();
        }
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

在類加載器篇中有提到,這里初始化三個類加載器圈纺,分別是commonLoader秦忿,catalinaLoader,sharedLoader并建立他們之間的關(guān)系蛾娶,catalinaLoader和sharedLoader的parent是commonLoader小渊。

這三個類加載器都是通過createClassLoader建立的:

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {

    String value = CatalinaProperties.getProperty(name + ".loader");
    if ((value == null) || (value.equals("")))
        return parent;

    value = replace(value);

    List<Repository> repositories = new ArrayList<>();

    String[] repositoryPaths = getPaths(value);

    for (String repository : repositoryPaths) {
        // Check for a JAR URL repository
        try {
            @SuppressWarnings("unused")
            URL url = new URL(repository);
            repositories.add(
                    new Repository(repository, RepositoryType.URL));
            continue;
        } catch (MalformedURLException e) {
            // Ignore
        }

        // Local repository
        if (repository.endsWith("*.jar")) {
            repository = repository.substring
                (0, repository.length() - "*.jar".length());
            repositories.add(
                    new Repository(repository, RepositoryType.GLOB));
        } else if (repository.endsWith(".jar")) {
            repositories.add(
                    new Repository(repository, RepositoryType.JAR));
        } else {
            repositories.add(
                    new Repository(repository, RepositoryType.DIR));
        }
    }

    return ClassLoaderFactory.createClassLoader(repositories, parent);
}

createClassLoader首先回去讀取CatalinaProperties中的common.loader,server.loader茫叭,shared.loader三個屬性酬屉,進(jìn)入CatalinaProperties類中會發(fā)現(xiàn)這三個屬性來自conf/catalina.properties文件。

接著往下createClassLoader會將common.loader,server.loader呐萨,shared.loader三個屬性中的值獲取然后解析成Repository杀饵,然后交給ClassLoaderFactory.createClassLoader方法去創(chuàng)建類加載器,最后可以實(shí)現(xiàn)三個不同的類加載器分別加載不同目錄下的類谬擦。

當(dāng)然要說清楚的是切距,默認(rèn)情況下,catalina.properties中server.loader惨远,shared.loader并沒有配置值谜悟,三個類加載是同一個,默認(rèn)加載{catalina.home}/lib目錄下的類和jar包北秽。

如果想配置對所有web應(yīng)用都可見但對tomcat內(nèi)部服務(wù)器不可見的類葡幸,此時(shí)應(yīng)該在catalina.properties文件中的shared.loader下進(jìn)行配置。

Thread.currentThread().setContextClassLoader(catalinaLoader)將catalinaLoader設(shè)置為Tomcat主線程的線程上下文類加載器贺氓。

3.2蔚叨、SecurityClassLoad.securityClassLoad(catalinaLoader)

SecurityClassLoad.securityClassLoad用于線程安全的加載tomcat容器所需的class。

當(dāng)然辙培,要使這個方法真正起作用蔑水,需要啟動tomcat安全管理器,由代碼可知:

public static void securityClassLoad(ClassLoader loader) throws Exception {
    securityClassLoad(loader, true);
}


static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager)
        throws Exception {

    if (requireSecurityManager && System.getSecurityManager() == null) {
        return;
    }

    loadCorePackage(loader);
    loadCoyotePackage(loader);
    loadLoaderPackage(loader);
    loadRealmPackage(loader);
    loadServletsPackage(loader);
    loadSessionPackage(loader);
    loadUtilPackage(loader);
    loadJavaxPackage(loader);
    loadConnectorPackage(loader);
    loadTomcatPackage(loader);
}

如果沒啟用安全管理器扬蕊,System.getSecurityManager()=null搀别,直接return。

可以通過命令行的方式啟功安全管理器:
catalina.bat run -security或者startup.bat -security

一旦啟動了安全管理器尾抑,就會根據(jù)conf/catalina.policy文件定義的提供默認(rèn)的安全策略歇父,securityClassLoad方法中System.getSecurityManager()不再等于null,于是就會去執(zhí)行一系列加載方法蛮穿,將tomcat的class加載進(jìn)來。

想了解to嗎t(yī)omcat的安全策略毁渗,可以參考下這篇博文:

你很少使用的安全管理SecurityManager

3.3践磅、通過反射實(shí)例化catalina

Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();

// Set the shared extensions class loader
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
    startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);

catalinaDaemon = startupInstance;

可以看到這部分代碼是通過反射來實(shí)例化catalina,然后反射調(diào)用setParentClassLoader將sharedLoader傳入到catalina實(shí)例中灸异。

為什么不直接通過Bootstrap類直接啟動tomcat府适,而是通過反射生成Catalina實(shí)例啟動?

再查看tomcat目錄結(jié)構(gòu)時(shí)應(yīng)該發(fā)現(xiàn)肺樟,Bootstrap并不在$CATALINA_HOME/lib目錄下檐春,而是在$CATALINA_HOME/bin目錄中,Bootstrap和Catalina松耦合(通過反射調(diào)用Catalina)么伯,它直接依賴JRE運(yùn)行并為Tomcat應(yīng)用服務(wù)器創(chuàng)建共享類加載器疟暖,用于構(gòu)造Catalina和整個tomcat服務(wù)器。實(shí)現(xiàn)了啟動入口和核心環(huán)境的解耦,簡化了啟動俐巴。

四骨望、setAwait

來看setAwait:

public void setAwait(boolean await)
        throws Exception {

    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Boolean.TYPE;
    Object paramValues[] = new Object[1];
    paramValues[0] = Boolean.valueOf(await);
    Method method =
        catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
    method.invoke(catalinaDaemon, paramValues);

}

通過反射調(diào)用catalina的setAwait方法,其設(shè)置的值是留給后面用的欣舵,當(dāng)Catalina將Tomcat的所有組件啟動之后擎鸠,會檢查await屬性,如果為true缘圈,會調(diào)用Catalina.await()劣光,而Catalina.await()又會調(diào)用其內(nèi)部的Server的await()。

public void start() {

    ...
        
    if (await) {
        await();
        stop();
    }
}

Server.await()包含一個while循環(huán)糟把,此循環(huán)用于監(jiān)聽指定socket端口(默認(rèn)為8005)的連接绢涡,當(dāng)某個連接傳入的參數(shù)為”SHUTDOWN”(默認(rèn)為”SHUTDOWN”)時(shí),終止此while循環(huán)(端口號和終止while循環(huán)的參數(shù)糊饱,在server.xml的Server標(biāo)簽設(shè)置)垂寥。

Server.await()用來維持Bootstrap的main方法(main thread)處于運(yùn)行狀態(tài),而線程池中監(jiān)聽http請求的線程是守護(hù)線程(daemon thread)另锋。

當(dāng)Tomcat的指定端口接收到關(guān)閉命令時(shí)滞项,Server.await()內(nèi)的while循環(huán)終止,然后Catalina會調(diào)用stop()方法夭坪,關(guān)閉Tomcat的所有組件文判,最終Bootstrap的main thread終止,Tomcat關(guān)閉室梅。

五戏仓、load

private void load(String[] arguments)
        throws Exception {

    // Call the load() method
    String methodName = "load";
    Object param[];
    Class<?> paramTypes[];
    if (arguments==null || arguments.length==0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    if (log.isDebugEnabled())
        log.debug("Calling startup class " + method);
    method.invoke(catalinaDaemon, param);

}

該方法通過反射調(diào)用catalina的load方法。

catalina的load方法首先初始化目錄(initDirs)和初始化命名服務(wù)(initNaming)亡鼠,然后是createStartDigester(要了解這個方法赏殃,應(yīng)該先了解一下Digester,它是apache的一個開源組件间涵,通過它可以很方便的從xml文件生成java對象)仁热,該方法初始化Digester,為Xml的標(biāo)簽即解析模式增加處理規(guī)則rule勾哩。

關(guān)于Digester抗蠢,可以參考這篇博文:

利用Digester解析xml文件

來看createStartDigester方法:

protected Digester createStartDigester() {
    long t1=System.currentTimeMillis();
    // Initialize the digester
    Digester digester = new Digester();
    digester.setValidating(false);
    digester.setRulesValidation(true);
    Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
    List<String> attrs = new ArrayList<>();
    attrs.add("className");
    fakeAttributes.put(Object.class, attrs);
    digester.setFakeAttributes(fakeAttributes);
    digester.setUseContextClassLoader(true);

    // Configure the actions we will be using
    digester.addObjectCreate("Server",
                             "org.apache.catalina.core.StandardServer",
                             "className");
    digester.addSetProperties("Server");
    digester.addSetNext("Server",
                        "setServer",
                        "org.apache.catalina.Server");

    digester.addObjectCreate("Server/GlobalNamingResources",
                             "org.apache.catalina.deploy.NamingResourcesImpl");
    digester.addSetProperties("Server/GlobalNamingResources");
    digester.addSetNext("Server/GlobalNamingResources",
                        "setGlobalNamingResources",
                        "org.apache.catalina.deploy.NamingResourcesImpl");

    digester.addObjectCreate("Server/Listener",
                             null, // MUST be specified in the element
                             "className");
    digester.addSetProperties("Server/Listener");
    digester.addSetNext("Server/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

    digester.addObjectCreate("Server/Service",
                             "org.apache.catalina.core.StandardService",
                             "className");
    digester.addSetProperties("Server/Service");
    digester.addSetNext("Server/Service",
                        "addService",
                        "org.apache.catalina.Service");

    digester.addObjectCreate("Server/Service/Listener",
                             null, // MUST be specified in the element
                             "className");
    digester.addSetProperties("Server/Service/Listener");
    digester.addSetNext("Server/Service/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

    //Executor
    digester.addObjectCreate("Server/Service/Executor",
                     "org.apache.catalina.core.StandardThreadExecutor",
                     "className");
    digester.addSetProperties("Server/Service/Executor");

    digester.addSetNext("Server/Service/Executor",
                        "addExecutor",
                        "org.apache.catalina.Executor");


    digester.addRule("Server/Service/Connector",
                     new ConnectorCreateRule());
    digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
            new String[]{"executor", "sslImplementationName", "protocol"}));
    digester.addSetNext("Server/Service/Connector",
                        "addConnector",
                        "org.apache.catalina.connector.Connector");

    digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                             "org.apache.tomcat.util.net.SSLHostConfig");
    digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
    digester.addSetNext("Server/Service/Connector/SSLHostConfig",
            "addSslHostConfig",
            "org.apache.tomcat.util.net.SSLHostConfig");

    digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                     new CertificateCreateRule());
    digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                     new SetAllPropertiesRule(new String[]{"type"}));
    digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                        "addCertificate",
                        "org.apache.tomcat.util.net.SSLHostConfigCertificate");

    digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                             "org.apache.tomcat.util.net.openssl.OpenSSLConf");
    digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
    digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                        "setOpenSslConf",
                        "org.apache.tomcat.util.net.openssl.OpenSSLConf");

    digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                             "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
    digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
    digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                        "addCmd",
                        "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");

    digester.addObjectCreate("Server/Service/Connector/Listener",
                             null, // MUST be specified in the element
                             "className");
    digester.addSetProperties("Server/Service/Connector/Listener");
    digester.addSetNext("Server/Service/Connector/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

    digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                              null, // MUST be specified in the element
                              "className");
    digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
    digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                        "addUpgradeProtocol",
                        "org.apache.coyote.UpgradeProtocol");

    // Add RuleSets for nested elements
    digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
    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/"));

    // When the 'engine' is found, set the parentClassLoader.
    digester.addRule("Server/Service/Engine",
                     new SetParentClassLoaderRule(parentClassLoader));
    addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

    long t2=System.currentTimeMillis();
    if (log.isDebugEnabled()) {
        log.debug("Digester for server.xml created " + ( t2-t1 ));
    }
    return digester;

}

該方法初始化digester,創(chuàng)建一系列解析規(guī)則思劳,然后在load方法中會調(diào)用:

digester.parse(inputSource);

可見digester解析的源是inputSource迅矛,而inputSource是來自于conf/server.xml:

file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());

configFile:

 protected File configFile() {

    //protected String configFile = "conf/server.xml";
    File file = new File(configFile);
    if (!file.isAbsolute()) {
        file = new File(Bootstrap.getCatalinaBase(), configFile);
    }
    return file;

}

這樣便創(chuàng)建出了StandardServer對象,接著便調(diào)用getServer().init();

init方法來自StandardServer的父類LifecycleBase:

public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}

具體實(shí)現(xiàn)是在子類的initInternal方法中潜叛,在調(diào)用initInternal方法前后都會設(shè)置狀態(tài)秽褒,LifecycleState.INITIALIZING代表正在初始化壶硅,LifecycleState.INITIALIZED表示初始化完成,相應(yīng)會觸發(fā)生命周期事件震嫉。

在StandardServer的initInternal方法中會調(diào)用子組件Services的init方法森瘪,并依次傳遞下去,完成所有組件的init票堵。

可見catalina的load方法主要是根據(jù)conf/server.xml配置文件利用Digester創(chuàng)建服務(wù)器組件扼睬,然后調(diào)用Server的init方法,逐層次的實(shí)現(xiàn)所有組件的初始化悴势。

六窗宇、start

最后看下start方法:

public void start()
        throws Exception {
    if( catalinaDaemon==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);

}

同樣也是通過反射調(diào)用catalina的start方法:

public void start() {

    if (getServer() == null) {
        load();
    }

    if (getServer() == null) {
        log.fatal("Cannot start server. Server instance is not configured.");
        return;
    }

    long t1 = System.nanoTime();

    // Start the new server
    try {
        getServer().start();
    } catch (LifecycleException e) {
        log.fatal(sm.getString("catalina.serverStartFail"), e);
        try {
            getServer().destroy();
        } catch (LifecycleException e1) {
            log.debug("destroy() failed for failed Server ", e1);
        }
        return;
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
    }

    // Register shutdown hook
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // If JULI is being used, disable JULI's shutdown hook since
        // shutdown hooks run in parallel and log messages may be lost
        // if JULI's hook completes before the CatalinaShutdownHook()
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                    false);
        }
    }

    if (await) {
        await();
        stop();
    }
}

該方法主要觸發(fā)StandardServer的start方法,StandardServer的start方法同init方法一樣來自LifecycleBase特纤,主要是改變生命周期的狀態(tài)军俊,同時(shí)觸發(fā)相應(yīng)的生命周期時(shí)間,具體的執(zhí)行邏輯交由具體的子類startInternal方法實(shí)現(xiàn):

public final synchronized void start() throws LifecycleException {

    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        return;
    }

    if (state.equals(LifecycleState.NEW)) {
        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());
    }
}

在StandardServer的startInternal方法中會調(diào)用子組件service的start方法捧存,并依次調(diào)用其他組件的start方法粪躬。

protected void startInternal() throws LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    // Start our defined Services
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

所以同load方法很相似,start方法主要是實(shí)現(xiàn)各組件的start方法依次調(diào)用昔穴,可以用一張圖來理解:

還應(yīng)該看到Catalina的start方法會使用前面的setAwait方法傳遞的值镰官,為true時(shí),會在8005端口監(jiān)聽吗货,保證主線程一直在運(yùn)行泳唠,直到收到SHUTDOWN命令。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宙搬,一起剝皮案震驚了整個濱河市笨腥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勇垛,老刑警劉巖脖母,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闲孤,居然都是意外死亡谆级,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門崭放,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哨苛,“玉大人鸽凶,你說我怎么就攤上這事币砂。” “怎么了玻侥?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵决摧,是天一觀的道長。 經(jīng)常有香客問我,道長掌桩,這世上最難降的妖魔是什么边锁? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮波岛,結(jié)果婚禮上茅坛,老公的妹妹穿的比我還像新娘。我一直安慰自己则拷,他們只是感情好贡蓖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煌茬,像睡著了一般斥铺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坛善,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天晾蜘,我揣著相機(jī)與錄音,去河邊找鬼眠屎。 笑死剔交,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的组力。 我是一名探鬼主播省容,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼燎字!你這毒婦竟也來了腥椒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤候衍,失蹤者是張志新(化名)和其女友劉穎笼蛛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛉鹿,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滨砍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妖异。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惋戏。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖他膳,靈堂內(nèi)的尸體忽然破棺而出响逢,到底是詐尸還是另有隱情,我是刑警寧澤棕孙,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布舔亭,位于F島的核電站些膨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钦铺。R本人自食惡果不足惜订雾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矛洞。 院中可真熱鬧洼哎,春花似錦、人聲如沸沼本。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擅威。三九已至壕探,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郊丛,已是汗流浹背李请。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厉熟,地道東北人导盅。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像揍瑟,于是被迫代替她去往敵國和親白翻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容