Tomcat8源码分析-启动流程-load方法

上一篇:Tomcat8源码分析-类结构图html

前面已经将Tomcat的类结构和架构介绍了一下,如今经过DEBUG代码看看代码是如何一层层调用的,下图为启动过程当中的准备+load详细过程java

 

详细调用过程-时序图

说明

    从整个图中能够观察到出现频次最高的就是init\initInternal\fireLifecycleEvent,在类图中也特地罗列了出来,说明主体流程就是靠这些方法来完成的。这三个方法的调用就是一个模板,即:init(LifecycleBase)->setStateInternal(LifecycleBase)->initInternal(StandardXxx)->setStateInternal(LifecycleBase),能够说几乎全部关键的实现都是在initInternal和LifecycleListener中完成的。web

    启动从一个BootStrap类开始,先执行它的静态块(完成了Tomcat工做目录初始化),BootStrap.main方法接着就调用BootStrap.init方法(肯定三个ClassLoader,Catalina类实例化),再而后就经过反射调用Catalina.load方法,开始了加载过程。apache

     再还没看源码以前,猜测一下Tomcat启动过程当中是否是应该有以下这些动做:设计模式

        1.获取server.xml缓存

        2.解析server.xml网络

        3.处理Tomcat本身内部默认的一些配置或者约束或者规则什么的多线程

        4.根据解析的结构生成Tomcat架构对应的实例结构 架构

        5.既然要接收请求,须要绑定与监听端口app

        6.找到目录下有哪些应用

        7.解析每一个应用下的web.xml文件

        8.处理Servlet\Servlet-Mappping\Filter

        9.等一切都准备就绪,Tomcat如何接收到请求,将请求封装为request,将request传给谁处理,处理以后又如何封装为request,如何发送回客户端

        10.经过什么BIO\NIO\NIO2等等IO方式完成网络操做

    下面就看看load过程当中有作的事情,包含了以上那些步骤。

虽然知道有一些StandardXxx类,可是稍微想一想也不会是由这些类去完成server.xml的定位和解析,由于它本身自己就须要解析配置完成,那么在类图中能够看到是有Catalina.configFile()这行代码,最终看到了熟悉的"server.xml",看到这里是否是接下来就应该执行解析了喃?往下看。。。

file = configFile();

    protected File configFile() {

        File file = new File(configFile);
        if (!file.isAbsolute()) {
            file = new File(Bootstrap.getCatalinaBase(), configFile);
        }
        return file;

    }

    protected String configFile = "conf/server.xml";

代码继续往下翻以前能够先看看这段代码:

Digester digester = createStartDigester()

Digester(有道词典翻译是蒸煮器,煮解器),我就把它叫作解析器吧,毕竟它后面又用parse()完成对server.xml的解析。刚点进去看的时候,有点被吓着了,都是一大堆不认识的鬼。。。摘了部分代码以下:

digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        //这里面还会添加不少规则,好比设置EngineConfig
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        //这里面还会添加不少规则,好比设置HostConfig(它会负责完成Context组件实例化和应用部署)
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

这样看上去就不会那么恐怖了,见到了熟悉的StandardServer,StandardService,能够猜测在解析的时候,是否是根据server.xml中的标签名字会从这里拿到对应类的全路径反射生成实例喃?看一张动图(3秒一帧)

接着往下,上面准备了规则(配置文件中对应标签应该实例化那些类,默认添加了那些监听器给对应的标签),找到了server.xml文件路径,接下来就要真正的解析和处理了,看代码

try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                //关键理解点2:将Catalina对象传给解析器,解析器完成以后,Server就已经有值了。如:
                // Server-Service-Engine-Host已经完成实例化。
                // Server-Service-Connector-(HTTP/AJP)ProtocolHandler-EndPoint
                //将断点放到这里看先后server的层级,能够匹配上Tomcat架构图
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
                ...省略....
            } catch (Exception e) {
                ...省略....
            }
        } finally {
            ...省略....
        }

这部分代码执行完,Server就已经实例化完了,但不是全部的内部属性都构造好了,还有很长的路要走好比init/start。

既然Server已经有了,再仔细看看它的内部结构,上动图

能够看到Server-Service-Engine-Host,Server-Service-Connector这两条线都已经有了,可能会问怎么还没看到Context-Wrapper喃,由于那是在start阶段完成的。

parse()方法里面的具体逻辑就不须要去剖析了,只须要知道它完成了Server-Service-Engine-Host等等的实例化,并添加了监听和Valve规则(这里看不懂不要紧,等后面就会知道了)。

到此为止,Server的内部结构雏形已经出来了,再看看往下的server.init-engint.init等等xxx.init是如何一层层调用的

代码往下走,看到了service.init

// Start the new server
        try {
            //Server Init操做,在这里会一层层的往下Init。Server-Service-Engine/Connector-Host
            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);
            }
        }

从这里开始就是模板方法设计模式开始发功的时候了,若是还不知道的话,赶忙点进去看看,并动手写写。有经验的话能够想起在JDK的不少源码都能看到这种设计模式,好比多线程中的同步器,还有Spring提供的BeanDefinitionRegistryPostProcessor扩展点等。

在看动图以前下描述一下:凡是LifecycleBase的子类,调用的init方法必定是LifecycleBase.init(由于该方法被声名为final了),里面就是setStateInternal-fireLifecycleEvent-initInternal-setStateInternal-fireLifecycleEvent这样的套路。看到这一点很重要,不然往下DEBUG脑袋会发蒙的。

下面这张动图描述了Server-Service-Engine-Host这条线,至于Server-Service-Engine-Connector这条线是一样的方式。

总结:

1、Catalina在调用StandardServer.init以前完成了对server.xml解析,生成的Server对象的内部层次结构,初始化了默认的一些参数对应的实例

2、StandardServer.init执行以后完成了以下几件事情:
1.Server-Service-Engine-Connector的状态都变成了INITIALIZED

2.Connector中HTTP/AJP协议对应的ProtocolHandler也都实例化完成,监听启动完成

如图:

但此时是没法访问Tom猫的主页的,由于还没start,也就是尚未部署应用。假设你尝试使用http://localhost:8080去访问,会一直没有响应。

3.MapperListener初始化完成

MapperListener经过监听容器事件来完成对容器的注册和取消注册。而Mapper用于对容器进行缓存和管理,同时提供uri映射的功能

在这里推荐两个博客:

深刻理解Tomcat

爱吃鱼的KK(里面有不少深刻的分析)

 

下一篇:Tomcat8源码分析-启动流程-start方法

相关文章
相关标签/搜索