勾勒一下Tomcat启动的流程,以便后续查找。(基于Tomcat 8.5.16)
Bootstrap
-
initClassLoaders()
创建以下类加载器,一般catalinaLoader 和sharedLoader 都默认和 commonLoader 为同一个-
commonLoader
加载 catalina 相关顶层公用的类,默认扫描CATALINA_HOME
,CATALINA_BASE
下的class -
catalinaLoader
加载tomcat 应用相关的类 -
sharedLoader
加载 Web 应用的类
-
catalinaLoader
被设置为 ContextClassLoader
- 创建
org.apache.catalina.startup.Catalina
。将Catalina.parentClassLoader
设置为sharedLoader
。依次调用Catalina.load()
和Catalina.start()
方法。
初始化 Initialisation
Catalina.load()
-
Catalina.load()
读取 conf/server.xml 中的配置作为Digester
的输入。根据配置生成相应的Server
,Listener
,Service
,Engine
,Host
,Context
等及其内部相关组件(如Valve
,Listener
)。
Engine
,Host
,Context
的配置读取逻辑被单独放在了EngineRuleSet
,HostRuleSet
,ContextRuleSet
中。特别需要关注伴随Host
,Context
生成的HostConfig
,ContextConfig
。(EngineConfig
没有实际内容略去)这两个 Config class 会监听 Container 的生命时间,作出许多关键配置。 -
getServer().init()
初始化 Server 组件
StandardServer.init()
-
StandardServer.initInternal()
遍历所有其services
调用init()
方法
StandardService.init()
-
StandardService.initInternal()
依次init 其管辖组件
Engine.init()
Executor.init()
MapperListener.init()
Connector.init()
LifecycleBase 的 start
方法中,会检查若 state == NEW 则先 init()
,Host, Context, Wrapper 均由此方法init
Context.init()
ContextConfig 会监听 AFTER_INIT_EVENT
, 并触发 ContextConfig.init()
- 创建
Digester
依照ContextRuleSet
读取配置文件并设置相应属性。
启动 Start
Catalina.start()
-
Catalina.start()
调用Server.start()
。StandardServer
遍历services
调用Service.start()
Service.start()
-
Service.start()
依次 start 其管辖组件
Engine.start()
Executor.start()
MapperListener.start()
Connector.start()
MapperListener
- 循环向engine 及其child containers 注册自己为 listener, 以监听containers 的变动。
- 循环遍历 engine 及其child containers, 注册进
Mapper
里。
StandardThreadExecutor
backed by a ThreadPoolExecutor
, with default minSpareThreads = 25, maxThreads = 200. maxQueueSize = Integer.MAX_VALUE
- 生成对应的ThreadPoolExecutor.
Connector
见 Tomcat请求处理流程
Containers
ContainerBase.startInternal()
依次执行
- 向
startStopExecutor
提交start()
所有子container的任务并等待完成。(startStopExecutor
就是一个普通的线程池)。 - start pipeline
- 在deamon线程上start
ContainerBackgroundProcessor
,会定期遍历调用自己和子类的backgroundProcess()
方法。
HostConfig
监听了Host 生命周期事件,实现了Context 的动态部署。
- 监听 Host 的
START_EVENT
事件
若设置了 deployOnStartup (默认为true),则会执行deployApps()
- 监听 Host
PERIODIC_EVENT
事件
如果设置了autoDeploy
(默认为true),则会检查文件更新并执行deployApps()
- 检查加载的Application 是否有更新(通过文件的lastModified 来检查)
- 检查是否存在旧版本的Application 可以 undeploy,是则undeploy
- 调用
deployApps()
尝试搜索及deploy 新的 application。
deployApps()
依次
- 执行
deployDescriptors()
搜索$CATALINA_BASE/{engine_name}/{host_name}
下的 xml 文件作为context config 进行部署 - 执行
deployWARs()
搜索部署appbase
下的所有 war - 执行
deployDirectories()
搜索部署appbase
下的所有目录
deployWARs()
和deployDirectories()
会以目录下的META-INF/context.xml
作为context的配置,deployDescriptors()
以 $CATALINA_BASE/{engine_name}/{host_name}/*.xml
作为配置,生成对应的 StandardContext
, 并调用 addChild()
方法,添加到当前host。
值得一提的是addChild()
的过程中会 start 子容器,而start 方法中会检查容器状态,适时 init()
容器。如此完成Context 的动态加载。
Context.start()
简要列举Context.start()
的步骤
- start WebResourceRoot
-
new WebappLoader(getParentClassLoader())
生成 WebappLoader。 - start WebappLoader, 创建webapp 级别的classloader
ParallelWebappClassLoader
- 设置
ParallelWebappClassLoader
为当前 ContextClassLoader - 发出
CONFIGURE_START_EVENT
事件。 - start child container 和 pipeline
- 调用
initializers
里所有ServletContainerInitializer
的onStartup
方法 listenerStart()
filterStart()
-
loadOnStartup(findChildren())
实例化并init 所有 load on startup 的servlet - 将 ContextClassLoader 设置回去
ContextConfig.configureStart()
ContextConfig 监听 CONFIGURE_START_EVENT
,调用configureStart()
以对Context 进行配置。configureStart()
调用 webConfig()
完成配置
- 解析global, host level 的web-fragments
-
processServletContainerInitializers()
搜索所有的ServletContainerInitializers
。这里使用了ClassLoader.getResources()
来搜索所有的META-INF/services/javax.servlet.ServletContainerInitializer
资源 - 把web-fragments merge 进web.xml 配置里
-
configureContext()
利用merge 完的配置再次配置 Context。 - 将web-fragment 相关的 META-INF/resource 添加进当前 Context
- 把之前搜索到的
ServletContainerInitializer
添加到 Context.