Tomcat8.5.43源碼分析-(2)Tomcat啓動過程探究 第一部分

首先,我們複製啓動Tomcat時候Console窗口打印的日誌,可以見到一些關鍵路徑,個人認爲比較重要的地方標註了出來:

八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server version:        Apache Tomcat/@VERSION@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server built:          @VERSION_BUILT@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Server number:         @VERSION_NUMBER@
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Name:               Windows 7
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: OS Version:            6.1
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Architecture:          amd64
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Java Home:             D:\Java\jdk1.8.0_131\jre
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Version:           1.8.0_131-b11
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: JVM Vendor:            Oracle Corporation
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_BASE:         D:\Tomcat\apache-tomcat-8.5.43-src
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: CATALINA_HOME:         D:\Tomcat\apache-tomcat-8.5.43-src
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:60317
八月 14, 2019 3:23:50 下午 org.apache.catalina.startup.VersionLoggerListener log
信息: Command line argument: -Dfile.encoding=UTF-8
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener lifecycleEvent
信息: APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
八月 14, 2019 3:23:50 下午 org.apache.catalina.core.AprLifecycleListener initializeSSL
信息: OpenSSL successfully initialized [OpenSSL 1.1.1c  28 May 2019]
八月 14, 2019 3:23:57 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-8080"]
八月 14, 2019 3:24:12 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
八月 14, 2019 3:24:12 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["ajp-nio-8009"]
八月 14, 2019 3:24:12 下午 org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
信息: Using a shared selector for servlet write/read
八月 14, 2019 3:24:12 下午 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 22907 ms
八月 14, 2019 3:24:13 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Catalina]
八月 14, 2019 3:24:13 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/@VERSION@
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\docs]
八月 14, 2019 3:24:13 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\docs] has finished in [597] ms
八月 14, 2019 3:24:13 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\examples]
八月 14, 2019 3:24:13 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: contextInitialized()
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextInitialized()
八月 14, 2019 3:24:14 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: attributeAdded('StockTicker', 'async.Stockticker@13bace10')
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\examples] has finished in [362] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\host-manager]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\host-manager] has finished in [62] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\manager]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\manager] has finished in [60] ms
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deploying web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\ROOT]
八月 14, 2019 3:24:14 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.HostConfig deployDirectory
信息: Deployment of web application directory [D:\Tomcat\apache-tomcat-8.5.43-src\webapps\ROOT] has finished in [48] ms
八月 14, 2019 3:24:14 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
八月 14, 2019 3:24:14 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-nio-8009"]
八月 14, 2019 3:24:14 下午 org.apache.catalina.startup.Catalina start
信息: Server startup in 1324 ms

開始讀代碼,定位到Tomcat的啓動類,Bootstrap的Main方法:

 /**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {
    	System.out.println("Tomcat is starting!");
        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();//1.初始化方法
            } 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);//2.讀取配置方法
                daemon.start();//3.啓動方法
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } 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);
        }

    }

關注到bootstrap.init()是初始化方法,看看到底初始化進行了哪些操作。

在探究init()方法之前,我們先關注BootStrap的靜態代碼塊。我們知道,在類首次加載的時候,靜態代碼塊會執行,且早於main方法。因此static代碼塊亦可以當做是初始化的一種:
 

static {
        // Will always be non-null
        String userDir = System.getProperty("user.dir");

        // Home first
        String home = System.getProperty(Globals.CATALINA_HOME_PROP);
        File homeFile = null;

        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        if (homeFile == null) {
            // First fall-back. See if current directory is a bin directory
            // in a normal Tomcat install
            File bootstrapJar = new File(userDir, "bootstrap.jar");

            if (bootstrapJar.exists()) {
                File f = new File(userDir, "..");
                try {
                    homeFile = f.getCanonicalFile();
                } catch (IOException ioe) {
                    homeFile = f.getAbsoluteFile();
                }
            }
        }

        if (homeFile == null) {
            // Second fall-back. Use current directory
            File f = new File(userDir);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        catalinaHomeFile = homeFile;
        System.setProperty(
                Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

        // Then base
        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
        if (base == null) {
            catalinaBaseFile = catalinaHomeFile;
        } else {
            File baseFile = new File(base);
            try {
                baseFile = baseFile.getCanonicalFile();
            } catch (IOException ioe) {
                baseFile = baseFile.getAbsoluteFile();
            }
            catalinaBaseFile = baseFile;
        }
        System.setProperty(
                Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }

static代碼塊實際上進行的是全局的兩個參數的配置:

  • Globals.CATALINA_HOME_PROP:  catalina.home- tomcat product installation path.

Home(安裝目錄):指向可共用目錄的父目錄,即bin和lib的父目錄。

  • Globals.CATALINA_BASE_PROP:catalina.base- tomcat instance installation path

Base(工作目錄):指向不可共用目錄的父目錄,即conf、logs、temp、webapps和work的父目錄。

在Tomcat7中,這個配置還是顯示進行的,可參考博文:https://www.cnblogs.com/huanghongbo/p/6127721.html

在當前版本Tomcat8.5.43中,已經使用靜態代碼塊來初始化了。

注意一下此處,System.getProperty(Globals.CATALINA_HOME_PROP) 會首先讀取環境變量中的配置。

目前還沒有碰到多個Tomcat實例的情況,故還沒實際使用個性化配置catalina_base。默認情況下catalina_base和catalina_home是一致的。

下面繼續探究Bootstrap的init()方法:

/**
     * Initialize daemon.
     * @throws Exception Fatal initialization error
     */
    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;//通過反射創建了Catalina實例,賦值catalinaDaemon

    }

init()方法爲Bootstrap初始化類加載器。Tomcat的類加載器具有隔離、共享的特性。多個類加載器各司其職,又相互隔離,使得Tomcat的類加載工作安全高效。具體的細節可參考《Tomcat架構解析》2.4.2節 Tomcat加載器一章。

initClassLoaders()方法初始化了3個類加載器,分別是:

  • Common-以JVM的類加載器爲父類,位於Tomcat類加載器的頂層,負責加載Tomcat應用服務器內部和Web應用均可見類。
  • Catalina-繼承Common類加載器,用於加載Tomcat應用服務器,負責加載只有Tomcat應用服務器內部可見類。
  • Shared-繼承Common類加載器,是所有Web應用的父加載器,負責加載Web 應用共享的類。

之後將initClassLoaders()中創建的catalinaLoader設置爲當前線程的類加載器。

Thread.currentThread().setContextClassLoader(catalinaLoader);

通過反射創建Catalina實例,賦值給catalinaDaemon。並設置 Catalina的父加載器爲initClassLoaders()中創建的sharedLoader。

可見init()主要有兩個目的:

  1. 設置Tomcat應用的類加載器;
  2. 生成Catalina實例,並設置其父加載器。

回到main方法,在初次創建時:

    /**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {
        ...
        try {
            String command = "start";
            ...
            
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
           ...
    }

看名字即可知道有兩個重要的方法,load()方法和start()方法。接下來我們就查看這兩個方法分別做了什麼。

daemon.load(args)實際上通過反射調用的是Catalina類的load()方法。

    /**
     * Start a new server instance.
     */
    public void load() {

        if (loaded) {
            return;
        }
        loaded = true;

        long t1 = System.nanoTime();

        initDirs();

        // Before digester - it may be needed
        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        //中間的一大串都是爲了找到server.xml
        try {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                        .getResourceAsStream(getConfigFile());
                    inputSource = new InputSource
                        (getClass().getClassLoader()
                         .getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                getConfigFile()), e);
                    }
                }
            }

            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually.
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                            .getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource
                    (getClass().getClassLoader()
                            .getResource("server-embed.xml").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                "server-embed.xml"), e);
                    }
                }
            }


            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString("catalina.configFail",
                            getConfigFile() + "] or [server-embed.xml]"));
                } else {
                    log.warn(sm.getString("catalina.configFail",
                            file.getAbsolutePath()));
                    if (file.exists() && !file.canRead()) {
                        log.warn("Permissions incorrect, read permission is not allowed on the file.");
                    }
                }
                return;
            }

            try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);//解析server.xml
            } catch (SAXParseException spe) {
                log.warn("Catalina.start using " + getConfigFile() + ": " +
                        spe.getMessage());
                return;
            } catch (Exception e) {
                log.warn("Catalina.start using " + getConfigFile() + ": " , e);
                return;
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }

        getServer().setCatalina(this);//StandardServer賦值Catalina
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }
    }
  • initDirs()-加載臨時文件夾地址
  • initNaming()-加載useNaming屬性,命名上下文
  • createStartDigester()-重點方法!構建Digester。後續通過Digester解析server.xml,創建Server、Service、Engine、Host、Context等容器的屬性及關係。參考《Tomcat架構解析》3.3章。

解析完Servel.xml之後,getServer()取到的就是createStartDigester()中的org.apache.catalina.core.StandardServer的實例了。

接着是另一個重點方法:

getServer().init()

StandardServer繼承自LifecycleBase這個抽象類。LifecycleBase是生命週期管理的重要抽象類。其init()方法:

    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) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }
    }

其中Lifecycle.BEFORE_INIT_EVENT爲生命週期中的事件;LifecycleState.INITIALIZING爲生命週期的階段。兩者的關係可以從以下代碼得到:

    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null);

setStateInternal方法中,會把生命週期事件通知給註冊的監聽器,在LifeCycleBase.fireLifecycleEvent中體現:

    /**
     * Allow sub classes to fire {@link Lifecycle} events.
     *
     * @param type  Event type
     * @param data  Data associated with event.
     */
    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }

回到LifecycleBase.init()方法,可以看到生命週期的通知事件都由LifecycleBase完成了。那麼繼承自LifecycleBase的類只需要關注自己的業務邏輯即可,實現抽象函數initInternal(),這裏相當於實現了一個切片。具體的方式,以此處爲例,getServer().init()就會執行StandardServer的initInternal():

/**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");

        // Register the naming resources
        globalNamingResources.init();

        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

這裏的重點方法爲

        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }

即StandardServer會遍歷執行Servel.xml中配置的Service,也就是StandardService類的init()方法。

同樣的StandardService也繼承自LifecycleBase,因此也只要關注其initInternal()方法即可:

    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        if (engine != null) {
            engine.init();
        }

        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize mapper listener
        mapperListener.init();

        // Initialize our defined Connectors
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

StandardService的重點方法有三個:

  • engine.init();  初始化Servlet引擎,引擎只負責請求的處理不需要考慮請求連接,協議等等。
  • executor.init();  初始化線程池,該線程池可以在組件中共享。
  • connector.init();  初始化連接器,connecto負責監聽、讀取請求,對請求進行制定協議的解析,匹配正確的處理容器,反饋響應。

由於篇幅太長,將在下一篇文章中繼續Tomcat的源碼解析之旅。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章