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

1. Tomcat启动时序图

先给出一张Tomcat标准启动时序图(标准启动是从 bin 中的启动文件启动 Tomcat):
Tomcat启动时序图
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 + 命令行参数
    startup.bat

2.2 catalina.bat

我们分片段看catalina.bat文件:
片段1:
catalina.bat片段1
catalina.bat片段2
catalina.bat片段3
catalina.bat片段4
catalina.bat片段5
catalina.bat片段6
catalina.bat片段7
catalina.bat片段8
catalina.bat片段9
最后tomcat启动是执行了Bootstrap类中的main方法。

补充下其他的方式,如debug等:
catalina.bat片段10

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过程:
Bootstrap.load()
接下来转到Catalina.load():
Catalina.load()
Server.init()会调用到StandardServer.initInternal():
StandardServer.initInternal()
Service.init()会调用到StandardService.initInternal():
StandardService.initInternal()
Engine.init()会调用到StandardEngine.init():
StandardEngine.init()


以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中,这个类负责统一的生命周期管理。

StandardServer 类图
LifecycleBase
所以 StandardServer 会调用到 initInternal(),这个方法会初始化子容器 Service 的 init()。
StandardServer 中 initInternal

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值