talinaProperties 类中获取 common.loader 等属性,获得类加载器的扫描仓库。CatalinaProperties 类在的静态块中调用了 loadProperties() 方法,从 conf/catalina.properties 文件中加载了属性.(即在类创建的时候属性就已经加载好了)。
通过 ClassLoaderFactory 创建 URLClassLoader 的实例
- 通过反射创建
Catalina 的实例并设置 parentClassLoader
setAwait(true) 。设置 Catalina 的 await 属性为 true 。在 Start 阶段尾部,若该属性为 true ,Tomcat 会在 main 线程中监听 SHUTDOWN 命令,默认端口是 8005.当收到该命令后执行 Catalina 的 stop() 方法关闭 Tomcat 服务器。
createStartDigester() 。Catalina 的该方法用于创建一个 Digester 实例,并添加解析 conf/server.xml 的 RuleSet 。Digester 原本是 Apache 的一个开源项目,专门解析 XML 文件的,但我看 Tomcat-9.0.0.M22 中直接将这些类整合到 Tomcat 内部了,而不是引入 jar 文件。Digester 工具的原理不在本文的介绍范围,有兴趣的话可以参考 The Digester Component – Apache 或 《How Tomcat works》- Digester [推荐] 一章
parse() 方法就是 Digester 处理 conf/server.xml 创建各个组件的过程。值的一提的是这些组件都是使用反射的方式来创建的。特别的,在创建 Digester 的时候,添加了一些特别的 rule Set ,用于创建一些十分核心的组件,这些组件在 conf/server.xml 中没有但是其作用都比较大,这里做下简单介绍,当 Start 时用到了再详细说明:
EngineConfig 。LifecycleListener 的实现类,触发 Engine 的生命周期事件后调用,这个监听器没有特别大的作用,就是打印一下日志
HostConfig 。LifecycleListener 的实现类,触发 Host 的生命周期事件后调用。这个监听器的作用就是部署应用程序,这包括 conf/<Engine>/<Host>/ 目录下所有的 Context xml 文件 和 webapps 目录下的应用程序,不管是 war 文件还是已解压的目录。 另外后台进程对应用程序的热部署也是由该监听器负责的。
ContextConfig 。LifecycleListener 的实现类,触发 Context 的生命周期事件时调用。这个监听器的作用是配置应用程序,它会读取并合并 conf/web.xml 和 应用程序的 web.xml ,分析 /WEB-INF/classes/ 和 /WEB-INF/lib/*.jar 中的 Class 文件的注解,将其中所有的 Servlet、ServletMapping、Filter、FilterMapping、Listener 都配置到 StandardContext 中,以备后期使用。当然了 web.xml 中还有一些其他的应用程序参数,最后都会一并配置到 StandardContext 中。
reconfigureStartStopExecutor() 用于重新配置启动和停止子容器的 Executor 。默认是 1 个线程。我们可以配置 conf/server.xml 中 Engine 的 startStopThreads ,来指定用于启动和停止子容器的线程数量,如果配置 0 的话会使用 Runtime.getRuntime().availableProcessors() 作为线程数,若配置为负数的话会使用 Runtime.getRuntime().availableProcessors() + 配置值 ,若和小与 1 的话,使用 1 作为线程数。当线程数是 1 时,使用 InlineExecutorService 它直接使用当前线程来执行启动停止操作,否则使用 ThreadPoolExecutor 来执行,其最大线程数为我们配置的值。
- 需要注意的是 Host 的
init 操作是在 Start 阶段来做的, StardardHost 创建好后其 state 属性的默认值是 LifecycleState.NEW ,所以在其调用 startInternal() 之前会进行一次初始化。
Tomcat Start[Deployment]
- 图中从
StandardHost Start StandardContext 的这步其实在真正的执行流程中会直接跳过,因为 conf/server.xml 文件中并没有配置任何的 Context ,所以在 findChildren() 查找子容器时会返回空数组,所以之后遍历子容器来启动子容器的 for 循环就直接跳过了。
- 触发 Host 的
BEFORE_START_EVENT 生命周期事件,HostConfig 调用其 beforeStart() 方法创建 $CATALINA_BASE/webapps & $CATALINA_BASE/conf/<Engine>/<Host>/ 目录。
- 触发 Host 的
START_EVENT 生命周期事件,HostConfig 调用其 start() 方法开始部署已在 $CATALINA_BASE/webapps & $CATALINA_BASE/conf/<Engine>/<Host>/ 目录下的应用程序。
- 解析
$CATALINA_BASE/conf/<Engine>/<Host&g |