Tomcat-启动流程

叨叨

讲真,最开始源码调试几遍下来还是很懵了,尤其是组件初始化过程中不断去调用其他组件的init()方法,中间使用模板方法的设计模式,最终抽象方法交给哪个子类去实现有点绕。这个搞明白之后,start()方法的流程就和init()类似了。过程中涉及的组件经过初步梳理,再结合server.xml中的标签,基本可以猜个七七八八,最后结合Tomcat的架构设计图进行梳理修正。
这里先记录下启动流程的学习。Tomcat源码的下载及相关配置就不赘述了,网上有很多教程。

程序入口

java程序的执行入口是main()方法,根据官网的启动流程描述或者启动脚本中的内容,可以定位到一个类:
org.apache.catalina.startup.Bootstrap

在这里插入图片描述

进到这个类中直接搜索main()方法,看下main()方法主要分为两大块:

  1. 初始化bootstrap
    进入init()方法可以看到是在做类加载器的初始化与设置
  2. 加载与启动bootstrap
    条件判断进入load()和start()的执行,按照猜想,在设置完类加载器后,框架启动的尿性就是读取并加载配置,初始化核心组件对象,然后启动框架。下面着重看下load和start两个方法。

在这里插入图片描述

加载load

流程简述

加载什么呢?无非就是:

  • 配置信息的加载(毕竟我们还有个配置文件server.xml)
  • 核心对象的加载

看下源码究竟做了什么:

Bootstrap.load()

通过反射调用Catalina.load()

Method method =
    catalinaDaemon.getClass().getMethod("load", paramTypes);
if (log.isDebugEnabled())
    log.debug("Calling startup class " + method);
System.out.println("******************* >>>   反射调用Catalina.load()");
method.invoke(catalinaDaemon, param);

Catalina.load(params)

首次看到load方法,直观感受:

  • 有几个init开头的方法,初始化可能会用到的资源
  • 创建了一个Digester对象,并解析输入源 digester.parse(inputSource)
  • 执行getServer().init()
// inputSource来自于一个文件
digester.parse(inputSource);
// 文件正是 configFile = "conf/server.xml";
file = configFile();

所以digester对象是用来解析server.xml的。
getServer()返回一个Server对象,server何时被set进Catalina对象中的?
最后调用server.init()开始初始化流程。

init()模板方法

init()方法是接口Lifecycle中的方法,可以看到init()方法在LifecycleBase类中实现,该类是一个抽象类,并且在init()方法的执行逻辑中调用抽象方法initInternal(),这不是妥妥的模板方法设计模式么。
所以这个initInternal应该是在某个server实现类A中执行的,且类A需要继承LifecycleBase或其子类。server只有一个实现类 StandardServer 且满足上述条件。

LifecycleBase中init模板方法:
在这里插入图片描述
StandardServer中的实现:

// The set of Services associated with this Server.
private Service services[] = new Service[0];


protected void initInternal() throws LifecycleException {
    // 调用父类
    super.initInternal();
    //...
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
    //...
}

super.initInternal()方法:
在这里插入图片描述

最开始看到这里时还是有点乱的,梳理之后就发现这样的层次逻辑很清晰。通常我们使用模板方法> 有两层:

第一层:抽象模板类
第二层:继承了抽象类的具体子类

Tomcat这里你可以理解成有三层:

第一层:抽象模板类
第二层:继承了抽象类的通用基础类LifecycleMBeanBase
第三层:继承了基础类的具体子类StandardXXX类

模板类中定义了模板方法init和抽象方法initInternal;基础类中实现initInternal时做了公共逻辑的抽> 取(即注册组件),最后在具体组件类中进行具体组件的初始化逻辑。

看一下类的继承关系:
在这里插入图片描述
再看一下在service组件的initInternal方法中,调用了engine,executor,mapperListener,connector四个组件的init()方法:

在具体组件类的初始化过程initInternal()中,都是通过super.initInternal()调用通用的组件注册逻
辑,最后再执行组件自身的初始化逻辑。

service中四个组件的init概览:

engine.init();
    1. Host组件的初始化并没有在此处执行,而是在start阶段进行的。(具体见start过程)
    2. engine的初始化主要做了如下操作:
    3. realm对象的处理
    4. 完成engine组件的注册

executor.init();
    完成executor组件的注册

mapperListener.init();
    完成listener组件的注册

connector.init();
    1. 创建coyote适配器
    2. protocolHandler.init() --> abstractProtocol.init() --> endpoint.init()

init流程总结

两load:
bootstrap.load:反射调用Catalina.load();
catalina.load:创建xml解析器解析server.xml配置,调用server.init()开始初始化;
Lifecycle实现类与模板方法模式:
通过定义Lifecycle接口以及使用模板方法,规范并统一了组件类的初始化过程。
需要注意:
Host等组件的初始化是在start过程中进行的。(见下说明)

start()模板方法

流程简述

组件的start()方法与init的流程类似,也是通过Lifecycle接口配合使用模板方法的模式完成组件的启动。
boostrap.start

通过反射调用Catalina.start()

Catalina.start

  • 检查server实例是否存在
  • getServer().start()启动server

start()模板方法

LifecycleBase中start模板方法:

public final synchronized void start() throws LifecycleException {
    System.out.println("******************* >>>   "+this.getClass().getName()+" 调用LifecycleBase.start");
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
        LifecycleState.STARTED.equals(state)) {

        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }

        return;
    }
    // 检查组件状态,是否需要初始化。Host等主键的初始化就是在这里开始的
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
               !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        // 调用抽象模板的具体子类
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    }
}

protected abstract void startInternal() throws LifecycleException;

StandardServer中的实现:

protected void startInternal() throws LifecycleException {
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    // Start our defined Services
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

service的start():

与init过程类似,在start过程中,同样也是同时对多个组件进行start()方法调用。

StandardEngine.start

直接调用父类ContainerBase.startInternal()方法,启动子组件和子容器;
子容器组件的启动通过线程池
在这里插入图片描述
StandardContext.startInternal
在这里插入图片描述

Host.start()特别说明

按照上面几个组件的加载机制以及server.xml中标签的层次结构,engine初始化时应该调用host的初始化方法,网上很多教程帖子的启动时序图也都是那样画的。这跟源码中的init流程是不符的

在这里插入图片描述
在这里插入图片描述

于是我开始在load阶段逐行阅读,始终找不到哪里调用了Host的init方法,最后在日志中找到答案:

在这里插入图片描述

启动流程之后细说,这里只需要知道:

  1. engine会将子标签(server.xml标签层级)对象遍历出来构建成可运行的线程丢到线程
    在这里插入图片描述
  2. 这些子标签组件会调用自身的start()方法启动
    在这里插入图片描述
  3. 组件启动时会检查组件状态,如果为NEW则会执行组件init方法
    在这里插入图片描述
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值