Tomcat8.5.43源码分析-(2)Tomcat启动过程探究 第一部分

本文详细解析了Tomcat启动过程中的关键步骤,包括初始化类加载器、解析server.xml配置文件、构建容器对象以及启动服务。深入探讨了Bootstrap、Catalina、StandardServer等核心组件的作用与交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,我们复制启动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():初始化线程池,该线程池可以在组件中共享。
  • mapperListener.init():初始化监听路由。
  • connector.init():初始化连接器,connecto负责监听、读取请求,对请求进行制定协议的解析,匹配正确的处理容器,反馈响应。

由于篇幅太长,将在下一篇文章中继续Tomcat的源码解析之旅。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值