Tomcat之启动过程源码分析

LifecycleBase生命周期抽象类

在分析启动流程之前,有必要对于实现了LifecycleBase这个类的所有类进行分析。结论是只要调用生命周期方法,都会先调用这个LifecycleBase的父类方法。以StandardServer和StandardEngine容器类为例

1、StandardServer

在具体分析之前需要看下StandardServer的继承关系图


image.png

StandardServer继承了生命周期抽象类LifecycleMBeanBase,LifecycleMBeanBase继承了LifecycleBase抽象类。由于StandardServer没有init、start、stop、destroy等方法,所以会到父类寻找这些方法。


image.png

①、init
org.apache.catalina.util.LifecycleBase#init
image.png

调用setStateInternal方法
发布LifecycleState.INITIALIZING是before_init的生命周期事件
②、setStateInternal
org.apache.catalina.util.LifecycleBase#setStateInternal


image.png

image.png

image.png

这个方法最重要的实现逻辑就是发布当前事件
③、fireLifecycleEvent
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
image.png

根据传入类型type = "before_init"获取当前事件,然后调用当前容器包含的LifecycleListener的lifecycleEvent方法。
④、initInternal

initInternal方法会调用到当前容器的initInternal方法。


image.png
2、StandardEngine继承图

继承了ContainerBase,ContainerBase继承了LifecycleMBeanBase并实现了Container。
LifecycleMBeanBase继承了LifecycleBase抽象类。


image.png

①、start
org.apache.catalina.util.LifecycleBase#start


image.png

发布LifecycleState.STARTING_PREP是before_start事件
image.png

②、startInternal

然后调用startInternal方法


image.png
Lifecycle有哪些LifecycleState事件

before_init、after_init、start、before_start、after_start、stop、before_stop、after_stop、after_destroy、before_destroy、periodic、configure_start、configure_stop
所以,任何容器类StandardService、StandardEngine、StandardHost、StandardContext、StandardWrapper及其他实现了LifecycleBase调用生命周期方法都是这样的调用流程

Bootstrap启动Tomcat

Tomcat启动通过Bootstrap类main方法启动


image.png

org.apache.catalina.startup.Bootstrap#main


image.png

image.png

main方法主要步骤有三步:

①、init()
②、load()
③、start()

1、init

org.apache.catalina.startup.Bootstrap#init()


image.png

①、初始化类加载器
org.apache.catalina.startup.Bootstrap#initClassLoaders


image.png

commonLoader、catalinaLoader、sharedLoader类加载器都是URLClassLoader
当前线程设置类加载器catalinaLoader
image.png

②、启动类加载并创建实例对象Catalina
设置Catalina类的父加载器setParentClassLoader是URLClassLoader


image.png

setAwait

catalina设置Await是阻塞,true表示阻塞


image.png

2、load

org.apache.catalina.startup.Bootstrap#load


image.png

org.apache.catalina.startup.Catalina#load()


image.png

①、创建Digester

获取file文件conf\server.xml,创建Digester对象。这个类的目的就是把server.xml中的层级结构和生命周期实现类都创建出来,包括容器类和监听器类。


image.png

org.apache.catalina.startup.Catalina#createStartDigester
digester对象是解析server.xml文件


image.png

fakeAttributes通过键值对添加
image.png

digester的规则添加到Rules对象


image.png

digester解析文件reader = SAXParserImpl$JAXPSAXParser
image.png

image.png

image.png

②、server.init初始化

设置server的Catalina属性


image.png

开始server的init
容器对象都实现了LifecycleBase声明周期方法,所以容器对象都会调用到org.apache.catalina.util.LifecycleBase#init,然后调用到容器的initInternal方法


image.png

org.apache.catalina.core.StandardServer#initInternal
image.png
③、services.init()

org.apache.catalina.util.LifecycleBase#init


image.png

org.apache.catalina.core.StandardService#initInternal


image.png

image.png
④、engine.init()

org.apache.catalina.core.StandardEngine#initInternal


image.png

org.apache.catalina.core.StandardEngine#getRealm


image.png

org.apache.catalina.core.ContainerBase#initInternal
image.png

org.apache.catalina.core.ContainerBase#reconfigureStartStopExecutor
创建内嵌的startStopExecutor执行器,用来处理部分容器类


image.png
⑤、executor.init()和mapperListener..init()

如果有自定义executors将会进行初始化


image.png
⑥、connector.init()

初始化连接
初始化adapter
org.apache.catalina.connector.Connector#initInternal


image.png

protocolHandler = Http11NioProtocol验证是否有效


image.png

image.png

org.apache.coyote.http11.AbstractHttp11Protocol#init
image.png

org.apache.coyote.AbstractProtocol#init
image.png

org.apache.tomcat.util.net.AbstractEndpoint#init


image.png

org.apache.tomcat.util.net.AbstractEndpoint#bindWithCleanup
image.png

org.apache.tomcat.util.net.NioEndpoint#bind
image.png

org.apache.tomcat.util.net.NioEndpoint#initServerSocket
创建serverSock对象,绑定端口,设置为非阻塞。服务端sockets启动
image.png

到这里便开启服务端Socket等待客户端连接

3、start

org.apache.catalina.startup.Bootstrap#start


image.png
3.1、StandardServer启动start

org.apache.catalina.startup.Catalina#start


image.png

这里也会调用到父类LifecycleBase#start方法
org.apache.catalina.util.LifecycleBase#start


image.png

org.apache.catalina.core.StandardServer#startInternal
3.2、StandardService启动start

开启services


image.png

开启一个startPeriodicLifecycleEvent,用来后台执行进行热加载和热部署


image.png

此时处于StandardService阶段,这里的lifecycleListeners 数量是0,所以setState方法什么都没做
image.png
3.3、StandardEngine启动start

开启engine


image.png

开启engine后


image.png

主要步骤是:executor、mapperListener、connector的开启
org.apache.catalina.core.StandardEngine#startInternal

在StandardEngine阶段,lifecycleListeners有EngineConfig监听器


image.png

org.apache.catalina.core.ContainerBase#startInternal
image.png

步骤包括:cluster、realm的start调用、获取孩子容器StandardHost
主要分析获取的孩子容器StandardHost
把得到的孩子容器封装成StartChild任务类,使用startStopExecutor线程池开启任务
image.png
3.3.1、executor启动start

executors线程池为null这里没有做什么

3.3.2、mapperListener启动start
image.png

org.apache.catalina.mapper.MapperListener#startInternal


image.png

org.apache.catalina.mapper.MapperListener#addListeners
listeners和lifecycleListeners添加MapperListener监听器,然后递归调用为孩子容器都添加这个MapperListener监听器


image.png

image.png

org.apache.catalina.mapper.MapperListener#registerHost
image.png

org.apache.catalina.mapper.MapperListener#registerContext


image.png

image.png

org.apache.catalina.mapper.MapperListener#prepareWrapperMappingInfo
对每个wrapper寻找对应的映射关系并添加到wrappers中
image.png

image.png

image.png
3.3.3、connector启动start

org.apache.catalina.core.StandardService#startInternal


image.png

org.apache.catalina.connector.Connector#startInternal


image.png

org.apache.coyote.AbstractProtocol#start
image.png

org.apache.tomcat.util.net.AbstractEndpoint#start


image.png

org.apache.tomcat.util.net.NioEndpoint#startInternal
image.png

创建executor工作线程池
image.png

poller线程启动
acceptor线程启动
image.png
3.4、StandardHost启动start

org.apache.catalina.core.ContainerBase.StartChild#call


image.png

lifecycleListeners包含HostConfig,所以在执行start之前
org.apache.catalina.util.LifecycleBase#start


image.png

org.apache.catalina.util.LifecycleBase#setStateInternal
发布一个lifecycleEvent = "before_start"事件
image.png

org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
HostConfig监听器处理before_start事件


image.png

org.apache.catalina.startup.HostConfig#lifecycleEvent
image.png

check()方法是periodic事件,用来热部署。beforeStart就是before_start事件
org.apache.catalina.startup.HostConfig#beforeStart
image.png

得到webapps和conf/Catalina/loaclhost文件夹
org.apache.catalina.core.StandardHost#startInternal
image.png

pipeline添加阈值通过addValve
image.png

调用父类方法
StandardHost[localhost]寻找孩子容器,与StandardEngine过程相同


image.png

封装成StartChild交给startStopExecutor线程池处理
3.5、StandardContext启动start
image.png

此时lifecycleListeners包括(CopyOnWriteArrayList 3个):ContextConfig、StandardHost$MemoryLeakTrackingListener 、ThreadLocalLeakPreventionListener
org.apache.catalina.util.LifecycleBase#setStateInternal
发布LifecycleState.STARTING_PREP事件是before_start


image.png

org.apache.catalina.util.LifecycleBase#fireLifecycleEvent


image.png

对三个监听器LifecycleListener调用lifecycleEvent
org.apache.catalina.startup.ContextConfig#lifecycleEvent
image.png

image.png

org.apache.catalina.startup.ContextConfig#fixDocBase

调整docBase


image.png

org.apache.catalina.startup.ContextConfig#antiLocking
对antiResourceLocking是true的处理,默认是false
image.png

另外两个LifecycleListener什么都没做
StandardContext.startInternal()

org.apache.catalina.core.StandardContext#startInternal
①、设置resources


image.png

org.apache.catalina.core.StandardContext#setResources


image.png

image.png

把传入的resources赋值给this(StandardContext)的resources
image.png

②、resources的启动
org.apache.catalina.core.StandardContext#resourcesStart


image.png

StandardRoot实现了LifecycleBase,所以直接到startInternal方法
org.apache.catalina.webresources.StandardRoot#startInternal
image.png

org.apache.catalina.webresources.StandardRoot#createMainResourceSet
创建包含当前Context的路径的WebResourceSet并返回
image.png

image.png

org.apache.catalina.webresources.StandardRoot#processWebInfLib
image.png

jar包资源resources也添加到StandradRoot的allResources中


image.png

image.png

③、创建webappLoader类,并设置到当前Context的loader中
image.png

image.png

④、当前应用加载器loader,启动start
image.png

WebappLoader实现了LifecycleBase
org.apache.catalina.loader.WebappLoader#startInternal
image.png

org.apache.catalina.loader.WebappLoader#createClassLoader
根据ParallelWebappClassLoader类的构造方法,传入父类加载器参是URLClassLoader。创建一个WebappClassLoaderBase对象。传入的resources是StandardRoot对象
image.png

image.png

org.apache.catalina.loader.WebappClassLoaderBase#start
把resources中的/WEB-INF/classes和/WEB-INF/lib目录添加到localRepositories中
image.png

image.png

⑤、解析web.xml文件
image.png

image.png

org.apache.catalina.startup.ContextConfig#lifecycleEvent
image.png

image.png

org.apache.catalina.startup.ContextConfig#webConfig


image.png

image.png

org.apache.tomcat.util.descriptor.web.WebXmlParser#parseWebXml(org.xml.sax.InputSource, org.apache.tomcat.util.descriptor.web.WebXml, boolean)
image.png

image.png

org.apache.tomcat.util.digester.Digester#parse(org.xml.sax.InputSource)
image.png

reader = SAXParserImpl$JAXPSAXParser
image.png

解析到servlets、servletMappings、 filters、 filterMaps等属性中
image.png

⑥、寻找ServletContainerInitializer实现
image.png

org.apache.catalina.startup.ContextConfig#processServletContainerInitializers
加载后的实现放入到detectedScis集合中
image.png

得到HandlesTypes注解
image.png

image.png

⑦、processClasses、合并web-fragment.xml、合并tomcat-web.xml
image.png

⑧、JSP处理、把web.xml配置到Context
org.apache.catalina.startup.ContextConfig#configureContext
配置filter、filterMap、listener
image.png

SecurityConstraint、role、ContextService
image.png

servlet设置
image.png

ServletMapping、sessionConfig
image.png

配置PostConstruct、PreDestroy方法
image.png

ServletContainerInitializer实现添加到context中
image.png

⑨、回调ServletContainerInitializers的onStartup方法
image.png
3.6、部署应用

发布LifecycleState.STARTING是start的事件
org.apache.catalina.core.ContainerBase#startInternal
这里是StandardHost容器阶段。时机是在开始Host,把Host的孩子容器节点封装到StartChild任务类,在线程池startStopExecutor调用的,然后调用results的get回调方法,pipeline开始方法之后调用的


image.png

image.png

org.apache.catalina.startup.HostConfig#lifecycleEvent


image.png

设置copyXML(false)、deployXML(true)、unpackWARs(true)、contextClass(org.apache.catalina.core.StandardContext)的参数值
image.png

org.apache.catalina.startup.HostConfig#start
image.png
deployApps()部署应用

org.apache.catalina.startup.HostConfig#deployApps()


image.png

①、deployDescriptors
org.apache.catalina.startup.HostConfig#deployDescriptors
处理部署在conf/Catalina/localhost/文件夹下XML形式的context


image.png

如果没有部署过则封装成DeployDescriptor任务类放到InlineExecutorService执行
②、deployWARs

org.apache.catalina.startup.HostConfig#deployWARs
部署war文件,webapps目录下以.war结尾的


image.png

如果没有部署过则封装成DeployWar任务类放到InlineExecutorService执行
image.png

③、deployDirectories
处理webapps目录下的文件夹
org.apache.catalina.startup.HostConfig#deployDirectories
image.png

如果没有部署过则封装成DeployDirectory任务类放到InlineExecutorService执行

总结:

Tomcat的启动时通过启动类的main方法,主要有三步:初始化(init)、加载(load)、启动(start)
1、初始化类加载器和Catalina类
2、根据server.xml创建Digester,所定义的节点都会生成对应的对象,节点所处的层级也会解析出来,设置不同容器的pipeline基础的Value阈值。server、services、engine、executor、mapperListener、connector的初始化调用,其中最重要的是endpoint的serverSocket的创建
3、StandardServer、StandardService和StandardEngine、StandardHost、StandardContext等容器的启动。
①、由于都实现了生命周期抽象类,所以会先调用LifecycleBase方法。在LifecycleBase方法内会使用调用setStateInternal发布一个事件,根据当前所处的容器阶段会调用相对应的监听器实现类。之后再调用所处容器的生命周期方法。
②、StandardHost和StandardContext都是在父类容器查找孩子容器然后封装成一个任务类放入到线程池中执行启动的。
③、在容器启动中,其中StandardContext的启动是最核心一个也是复杂的一个。主要包括WebappLoader类加载器创建和启动、解析web.xml文件(把解析后的值放到WebXML对象)、ServletContainerInitializers的查找及启动、把WebXML对象封装到当前Context对象等过程
④、容器启动后,就要部署应用。有三种部署形式,也都是封装成任务类放入到线程池内执行。部署应用分为部署server.xml中定义的Context和部署webapp文件夹下的Context

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350