Tomcat第二篇——启动过程

Posted 搬砖小松鼠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat第二篇——启动过程相关的知识,希望对你有一定的参考价值。

一、启动类

Tomcat的启动涉及到两个类:BootStrap和Catalina类。Catalina类用于启动和关闭Server对象,并通过Digester来解析conf目录下的Server.xml文件。BootStrap类则是一个入口类,入口是main方法。下面来看看源码中Tomcat的启动过程。

二、如何调试源码

这里介绍一种比较简单的方法:导入jar包调试法

找一个自己配过tomcat的web项目,在pom中引入Tomcat的嵌入jar包,如下图所示。其中版本设置为你自己下载的tomcat版本即可。

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>7.0.84</version>
</dependency>

三、启动过程

BootStrap类

首先在BootStrap类的main方法中打一个断点,启动项目就会停在如下断点:

1.调用init方法

BootStrap的init方法会做以下的事情

(1)设置Catalina的一些目录信息。setCatalinaHome、setCatalinaBase

(2)初始化类加载器。initClassLoaders会初始化Tomcat的三个类加载器:CommonClassLoader、CatalinaClassLoader、SharedClassLoader

(3)用CatalinaClassLoader加载Catalina类,并通过反射得到Catalina的实例,再通过反射设置父加载器,最后将catalina实例赋值给Daemon

执行完初始化之后,会回到main执行后续的逻辑

public void init()
        throws Exception
    

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        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.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;

    

2.依据传入的args类型,分别执行不同的动作方法

前面截图中已经看到参数是“start”,因此会走到如下逻辑:

3.接下来看看load和start中做了些什么

load方法中通过反射调用了daemon的load方法,也就是Catalina的load方法

start方法和load方法类似,这里不再赘述

总体看来,BootStrap做的工作也不多,初始化了几个类加载器,然后将所有的load、start等动作通过反射反映到Catalina上面

Catalina类

1.首先看下被Bootstrap调用的load方法

(1)创建一个Digester对象。createStartDigester方法里面点进去看看就是按照xml中配置的结构

(2)读取并解析配置文件。configFile默认就是server.xml,首先将当前对象Catalina设置为根对象

(3)解析完之后就得到了Server对象,并设置Server对象的Catalina参数未当前对象

(4)调用Server的init方法进行初始化。

Server对象是StandardServer的实例,它内部的initInternal方法会遍历该Server下的Service实例的init方法。

Service的initInternal方法又会初始化Container、Executor和Connectors。 

Connector会调用各个ProtocolHandler的init方法。Protocol是处理各种协议处理的父接口,里面又会调用各个处理协议的Endpoint的init方法,并绑定端口

这样依次各个组件都得到了初始化

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

        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;
        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);
         catch (SAXParseException spe) 
            log.warn("Catalina.start using " + getConfigFile() + ": " +
                    spe.getMessage());
            return;
         catch (Exception e) 
            log.warn("Catalina.start using " + getConfigFile() + ": " , e);
            return;
         finally 
            try 
                inputStream.close();
             catch (IOException e) 
                // Ignore
            
        

        getServer().setCatalina(this);

        // 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");
        

    

StandardServer中的initInternal方法:

StandardService中的initInternal方法:

Connector中的init方法,这里protocolHandler取决于xml中的配置,如处理bio的Http11Protocol

Protocolhandler的init实现是在AbstractProtocol

AbstractProtocol中的init实现

AbstractEndPoint中的init方法,调用bind

2.start方法和load中的init方法类似,不再赘述

对于Connector,各个EndPoint实现中会启动acceptor线程,每个Acceptor就会监听Sorket的连接

以上是关于Tomcat第二篇——启动过程的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat 第二篇:启动流程

javaweb回顾第二篇tomcat和web程序部署

使用docker部署tomcat|tomcat基础使用第二篇

第二篇:白话tornado源码之待请求阶段

第二篇:白话tornado源码之待请求阶段

JAVA企业应用第二篇TOMCAT安装