勾勒一下Tomcat启动的流程,以便后续查找。(基于Tomcat 8.5.16)
Bootstrap
initClassLoaders()创建以下类加载器,一般catalinaLoader 和sharedLoader 都默认和 commonLoader 为同一个
commonLoader加载 catalina 相关顶层公用的类,默认扫描CATALINA_HOME,CATALINA_BASE下的classcatalinaLoader加载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.
作者:lxian2shell
链接:https://www.jianshu.com/p/8a9f5a3ccaac
随时随地看视频