文档章节

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

特拉仔
 特拉仔
发布于 2019/12/04 12:07
字数 1747
阅读 44
收藏 0

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

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

 

详细调用过程-时序图

说明

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

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

     再还没看源码之前,猜想一下Tomcat启动过程中是不是应该有如下这些动作:

        1.获取server.xml

        2.解析server.xml

        3.处理Tomcat自己内部默认的一些配置或者约束或者规则什么的

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

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

        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这条线是同样的方式。

总结:

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

二、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方法

© 著作权归作者所有

特拉仔
粉丝 63
博文 279
码字总数 286987
作品 0
渝中
架构师
私信 提问
加载中

评论(0)

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

上一篇:Tomcat8源码分析-启动流程-load方法 前面讲了启动流程中的Catalina.load,进一步调用绝大部分组建的init操作,主要完成对server.xml解析,并根据解析的结果结合设置的Rule(规则)构造...

特拉仔
2019/12/08
48
0
Tomcat 源码分析(-)启动过程分析

前面几篇文章分别介绍了Tomcat的安装、优化和框架,本文主要用于分析Tomcat源码启动过程,研究一个框架最好的着手方式可能就是研究它的启动过程,因为在这过程中我们可以看到它内部的层次关系...

AaronSheng
2016/11/28
171
0
Tomcat8源码分析-请求处理过程(含源码中新建web应用一起调试)

上一篇:Tomcat8源码分析-启动流程-start方法 此篇主要将Tomcat8从接收请求到处理请求的时序图画出来,并用文字描述一下主要流程 时序图 图片有点大,需要点开了放大看 说明 文字描述流程之前...

特拉仔
2019/12/11
41
0
Android进阶——源码分析之图片加载框架Glide

前言 源码阅读不是一件简单的事情,每次都要花费很长时间来梳理其内部的原理,所以有朋友觉得看一遍看不懂,那很正常,接下来就是考验你耐心的时候。学习源码的目的是:学习它好的地方,吸收...

qq_30379689
2017/10/15
0
0
iOS逆向(5)-不知MachO怎敢说自己懂DYLD

在上篇文章代码注入,窃取微信密码中咱们已经简单的提到了MachO,在用Framework做代码注入的时候,必须先向MachO的Load Commons中插入该Framework的的相对路径,让我们的iPhone在执行MachO的...

一缕清风杨万里
2019/03/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

乳山哪里可以开医院门诊发票-中国新闻网

乳山哪里可以开医院门诊发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bri...

18281711164
今天
54
0
庄河哪里可以开医院门诊发票-中国新闻网

庄河哪里可以开医院门诊发票【152 * 9б 28 * 21 б9】陈生,诚、信、合、作,保、真、售、后、保、障、长、期、有、效。adb的全称为Android Debug Bri...

18249433684
今天
55
0
IntelliJ 如何找到项目中 Deprecated 的方法

在一个项目中,如果我们标记了某些元素为 Deprecated 的话,如何让我们能够快速找到? 简单来说,你可以对项目进行 Code Inspection。 选择 Analyze > Inspect Code 在弹出的对话框中,对整个...

honeymoose
今天
85
0
Java中的排序算法:冒泡排序

学习了一种新的排序算法:冒泡排序,冒泡排序是一种交换排序,指比较相邻的两个元素,如果前者比后者大,就交换位置,继续进行比较。 通过例子来实现: import java.util.Arrays; public cl...

北芷南姜
今天
73
0
OSChina 周五乱弹 —— 你不仅要背负工作,还要背负领导

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 《Cold Rain》- AniFace 手机党少年们想听歌,请使劲儿戳(这里) @明月依稀 :露...

小小编辑
今天
353
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部