写在前面的话:读 Tomcat 源码也有段时间了,大领悟谈不上。一些小心得记录下来,供大家参考相护学习。
一、启动流程
Tomcat 启动首先需要熟悉的是它的启动流程。和初学者第一天开始写 Hello World 一样,Tomcat 的启动也依赖 main 方法。
- /*
- * org.apache.catalina.startup.Bootstrap
- */
- if (daemon == null) {
- Bootstrap bootstrap = new Bootstrap(); // 实例对象
- try {
- bootstrap.init(); // 初始化
- } catch(Throwable t) {
- handleThrowable(t);
- return;
- }
- daemon = bootstrap;
- } else {
- Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
- }
实例化 Bootstrap 之后,首先需要对它初始化。初始化的流程很长,但是省略掉细节其实就是做了两件事:
(1)为当前线程创建类加载器:
(2)通过反射实例化一个 Catalina 对象(Tomcat 组件的实际管理者):
- initClassLoaders()
初始化以后 daemon 和 catalinaDaemon 就完成了赋值。继续 main 方法往下
- Class < ?>startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
- Object startupInstance = startupClass.newInstance();
- String command = "start";
- if (args.length > 0) {
- command = args[args.length - 1];
- }
- if (command.equals("startd")) {
- args[args.length - 1] = "start";
- daemon.load(args); // 加载
- daemon.start(); // 启动
- } else if (command.equals("stopd")) {
- args[args.length - 1] = "stop";
- daemon.stop();
- } else if (command.equals("start")) {
- daemon.setAwait(true);
- daemon.load(args);
- daemon.start();
- } else if (command.equals("stop")) {
- daemon.stopServer(args);
- }
首先执行加载,依然是使用反射调用 Catalina 对象的 load() 方法。方法里面的代码很多第一次看的同学肯定会有点蒙,仔细分析一下:
(1)读取 conf/server.xml 文件并解析 InputStream:
Digester 是一个 xml 文件解析工具,目前属于 Apache 的 Jakarta 项目。使用 Digester 解析 xml 文件代码量少也非常简单。
(2)初始化 Server
接下来我们看看 server.xml 的文档结构,里面包含了几个重要的节点:Server、Listener、Service、Connector、Engine、Realm、Host。初始化的过程其实就是将这些节点所代表的对象逐一初始化。Server 是其它节点的根节点,所以首先对它执行 init() 操作。
- Server serv = getServer();
- serv.init();
二、生命周期管理
Tomcat 中的许多对象都实现了 Lifecycle 接口,里面定义了 12 个状态,类关系图如下:
LifecycleBase 实现了 Lifecycle 并引入 LifecycleState 和 LifecycleSupport,LifecycleState 是一个枚举类,将 12 个状态除 NEW 和 FAILED 以外分别将状态和事件对应,并通过事件触发监听器。LifecycleSupport 集中管理各种监听器。Tomcat 通过 4 个方法管理这些状态:init() 、start()、stop()、destroy(),而 LifecycleBase 实现了 init() 、start()、stop()、destroy() 又暴露了 4 个接口 initInternal()、startInternal()、stopInternal()、destroyInternal()。所有生命周期对象都需要实现以上 4 个方法,是设计模式中的模板模式。
初略分析了一下 Tomcat 对于组件的管理方式,接下来再回到 Catalina 的 load() 方法。方法中通过 getServer() 获取 StandardServer 实例,并执行 initInternal() 方法。在 server.xml 配置文件中,server 节点可以包含多个 service
- for (int i = 0; i < services.length; i++) {
- services[i].init(); // 初始化StandardService
- }
在 StandardService 的 initInternal() 方法中继续初始化 Connector 和 Executor,重点代码如下:
当所有组件初始化完成之后执行启动流程:
- synchronized (connectorsLock) {
- for (Connector connector : connectors) {
- try {
- connector.init();
- } catch (Exception e) {
- String message = sm.getString(
- "standardService.connector.initFailed", connector);
- log.error(message, e);
- if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
- throw new LifecycleException(message);
- }
- }
- }
- try {
- // getServer().start();
- Server srv = getServer();
- srv.start(); // 启动流程...
- } catch(LifecycleException e) {
- log.fatal(sm.getString("catalina.serverStartFail"), e);
- try {
- getServer().destroy();
- } catch(LifecycleException e1) {
- log.debug("destroy() failed for failed Server ", e1);
- }
- return;
- }
和 initInternal() 流程类似,startInternal() 方法也是逐步调用,就不在文章中一一分析了。感兴趣的同学要想深入探究请通过源码 debug 分析。总结:Tomcat 启动流程实质上就是对各个生命周期组件的管理并通过 Digester 解析 xml 文件,这些组件在不同的生命周期状态又分别对应不同的响应事件,监听器通过响应事件驱动也方便了开发者的二次扩展值得认真学习。
来源: http://www.cnblogs.com/learnhow/p/8047869.html