02. Tomcat源代码—03. 启动流程分析

1. Tomcat启动时序图

先给出一张Tomcat标准启动时序图(标准启动是从 bin 中的启动文件启动 Tomcat):
02. Tomcat源代码—03. 启动流程分析_第1张图片
Tomcat 的启动非常标准,除去 Boostrap、Catalin,Server、Service 等这些组件与server.xml 都是一一对照,同时又有先后顺序。基本的顺序是先 init(),然后再 start()。

除了 Bootstrap、Catalina,其他的 Server、Service 等都只是一个接口,实现类均为 StandardXXX 。

下面按照从脚本启动的顺序,一步步分析Tomcat的启动过程。

2. 启动脚本

结论:startup.bat调用catalina.bat,传入start;catalina.bat在start分支中使用java Bootstrap 命令函参数 start启动Tomcat。
我们启动Tomcat时,是在Tomcat中bin目录中,点击startup.bat(如果是Windows的话,当然Linux使用startup.sh)。通过下面的分析知道,启动tomcat也可以不用startup.bat,直接调用catalina.bat start。

2.1 startup.bat

startup.bat做的事情是:

  1. 查找catalina.bat文件
  2. 调用catalina.bat,并传入start + 命令行参数
    02. Tomcat源代码—03. 启动流程分析_第2张图片

2.2 catalina.bat

我们分片段看catalina.bat文件:
片段1:
02. Tomcat源代码—03. 启动流程分析_第3张图片
02. Tomcat源代码—03. 启动流程分析_第4张图片
02. Tomcat源代码—03. 启动流程分析_第5张图片
02. Tomcat源代码—03. 启动流程分析_第6张图片
02. Tomcat源代码—03. 启动流程分析_第7张图片
02. Tomcat源代码—03. 启动流程分析_第8张图片
catalina.bat片段7
02. Tomcat源代码—03. 启动流程分析_第9张图片
02. Tomcat源代码—03. 启动流程分析_第10张图片
最后tomcat启动是执行了Bootstrap类中的main方法。

补充下其他的方式,如debug等:
02. Tomcat源代码—03. 启动流程分析_第11张图片

3. Bootstrap

通过上面脚本分析知道,Tomcat标准启动调用Bootstap.main(),传入参数是start。

Bootstrap.main()

public static void main(String args[]) {
     

   synchronized (daemonLock) {
     
       if (daemon == null) {
     
           // main第一次执行时,daemon为null,new Bootstrap(),并执行init。
           Bootstrap bootstrap = new Bootstrap();
           try {
     
               bootstrap.init();
           } catch (Throwable t) {
     
               handleThrowable(t); t.printStackTrace();
               return;
           }
           daemon = bootstrap;
       } else {
     
           Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
       }
   }

   try {
     
       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();
           if (null == daemon.getServer()) {
     
               System.exit(1);
           }
       } else if (command.equals("stop")) {
     
           daemon.stopServer(args);
       } else if (command.equals("configtest")) {
     
           daemon.load(args);
           if (null == daemon.getServer()) {
     
               System.exit(1);
           }
           System.exit(0);
       } else {
     
           log.warn("Bootstrap: command \"" + command + "\" does not exist.");
       }
   } catch (Throwable t) {
     
       // Unwrap the Exception for clearer error reporting
       if (t instanceof InvocationTargetException &&
               t.getCause() != null) {
     
           t = t.getCause();
       }
       handleThrowable(t);
       t.printStackTrace();
       System.exit(1);
   }
}

上面main()分为2块:

  1. init():创建Common ClassLoader、Catalina ClassLoader、Shared ClassLoader,并创建Catalina的实例,赋值给catalinaDaemon。详细见02. Tomcat源代码—04. 类加载器。
  2. load()、start():命令行传入的是start,因此看else if (command.equals("start"))分支。

下面先看load过程:
02. Tomcat源代码—03. 启动流程分析_第12张图片
接下来转到Catalina.load():
02. Tomcat源代码—03. 启动流程分析_第13张图片
Server.init()会调用到StandardServer.initInternal():
02. Tomcat源代码—03. 启动流程分析_第14张图片
Service.init()会调用到StandardService.initInternal():
02. Tomcat源代码—03. 启动流程分析_第15张图片
Engine.init()会调用到StandardEngine.init():
02. Tomcat源代码—03. 启动流程分析_第16张图片


以StandardServer类为例,发现没有init(),只有一个类似于 init 的 initInternal(),这是为什么呢?

@Override
protected void initInternal() throws LifecycleException {
     
	...
}

首先看下Tomcat各组件的生命周期管理。

2. 组件的生命周期管理

Tomcat 的架构设计是清晰的、模块化的。Tomcat拥有很多组件,在启动时一个一个组件启动,很容易遗漏组件,同时还会给后面动态组件拓展带来麻烦。如果采用传统方式的话,组件在启动过程中如果发生异常,会很难管理,比如下一个组件调用了 start(),但是如果它的上级组件还没有 start 甚至还没有 init 的话,Tomcat 的启动会非常难管理。

因此,Tomcat 的设计者提出一个解决方案:用 Lifecycle 管理启动,停止、关闭。

2.1 Lifecycle——生命周期统一接口

Tomcat 内部架构中各个核心组件有包含与被包含关系,如:Server 包含了 Service,Service 又包含了 Container、Connector,这个结构有一点像数据结构中的树。所以,我们可以通过父容器启动它的子容器,这样只要启动根容器,就可以把其他所有容器都启动,从而达到统一的启动,停止、关闭的效果。
所有这些组件有一个统一的接口——Lifecycle,把所有的启动、停止、关闭等生命周期相关的方法都组织到一起,可以很方便管理 Tomcat 各个容器组件的生命周期。

Lifecycle 其实就是定义了一些状态常量和几个方法,主要方法是 init、start、stop。Lifecycle 使用的是模板设计模式。

public interface Lifecycle {
     
    public static final String BEFORE_INIT_EVENT = "before_init";

    public static final String AFTER_INIT_EVENT = "after_init";

    public static final String START_EVENT = "start";

    public static final String BEFORE_START_EVENT = "before_start";

    public static final String AFTER_START_EVENT = "after_start";

    public static final String STOP_EVENT = "stop";

    public static final String BEFORE_STOP_EVENT = "before_stop";

    public static final String AFTER_STOP_EVENT = "after_stop";

    public static final String AFTER_DESTROY_EVENT = "after_destroy";

    public static final String BEFORE_DESTROY_EVENT = "before_destroy";

    public static final String PERIODIC_EVENT = "periodic";

    public static final String CONFIGURE_START_EVENT = "configure_start";

    public static final String CONFIGURE_STOP_EVENT = "configure_stop";

    public void addLifecycleListener(LifecycleListener listener);

    public LifecycleListener[] findLifecycleListeners();

    public void removeLifecycleListener(LifecycleListener listener);

    public void init() throws LifecycleException;

    public void start() throws LifecycleException;

    public void stop() throws LifecycleException;

    public void destroy() throws LifecycleException;

    public LifecycleState getState();

    public String getStateName();

    public interface SingleUse {
     
    }
}

Tomcat 的 Server 组件的 init() 负责遍历调用其包含所有的 Service 组件的 init(),但是Server 的实现类 StandardServer 没有 init(),那么init() 是在哪里? 其实是在它的父类 LifecycleBase中,这个类负责统一的生命周期管理。

02. Tomcat源代码—03. 启动流程分析_第17张图片
02. Tomcat源代码—03. 启动流程分析_第18张图片
所以 StandardServer 会调用到 initInternal(),这个方法会初始化子容器 Service 的 init()。
02. Tomcat源代码—03. 启动流程分析_第19张图片

你可能感兴趣的:(Tomcat,tomcat)