tomcat + spring mvc原理(二):tomcat容器动态加载

    tomcat + spring mvc 原理(一):tomcat原理综述比较详细的叙述了tomcat容器的静态结构和容器的配置,从tomcat动态运行来讲,我也在原理(一)中简单论述了:

显然,这种运作模式要求:tomcat需要有监视指定目录,一旦有新的war包加入,就完成解包并动态加载编译后的class的能力;tomcat需要有网络端口开闭和监视的机制,维护线程池来处理随时可能到来的请求;tomcat还需要将到来的请求顺利地传递给spring mvc服务,然后再将服务返回的数据发送出去。

    其实,对tomcat的理解,不只是应该了解静态容器结构,对于动态运行方面,应该包括:    

  1. 容器的初始加载和启动
  2. war包动态监视和加载
  3. 网络请求的监控和处理

    这样基本就涵盖了tomcat所有的基本运作原理。本文主要涉及第一部分:容器的初始加载与启动。

容器通用生命周期标准

    tomcat的容器,包括Server、Service、Connector、Engine、Host、Context、Wrapper都继承自同一个管理生命周期的接口:Lifecycle。
    首先,这个接口类定义了这些容器的初始化、启动、停止和销毁的标准生命周期方法。

public interface Lifecycle {
    ......
    //容器初始化
    public void init() throws LifecycleException;

    //容器启动
    public void start() throws LifecycleException;

    //容器停止
    public void stop() throws LifecycleException;

    //容器销毁
    public void destroy() throws LifecycleException;

    ......
}

    其次,这个接口类定义了这些容器所处生命周期的状态事件名和状态事件监听器的管理。

public interface Lifecycle {
    ......
   /**
    *13种生命周期状态事件的名字。通过名字还是比较比较好理解的
    **/
    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 LifecycleState getState();
    //获取状态名
    public String getStateName();


    /**
     *状态事件监听器。LifecycleListener中包含一个处理事件的方
     *法LifecycleEvent()
     **/
     //添加状态事件监听器
    public void addLifecycleListener(LifecycleListener listener);

     //获取所有状态事件监听器
    public LifecycleListener[] findLifecycleListeners();

    //移除状态事件监听器
    public void removeLifecycleListener(LifecycleListener listener);

    ......
}

容器通用生命周期的实现

    Lifecycle接口类的通用实现是在LifecycleBase类中。
    容器都继承自LifeCycleBase类,LifecycleBase类为容器提供了基本生命周期方法的一般实现和生命周期状态监听器的管理实现。

  • 生命周期状态监听器的管理实现

    这一部分主要是监听器管理的实现,但真正发挥作用是在生命周期方法的实现中。
    LifecycleBase中使用一个自己定义的List--CopyOnWriteArrayList来存储LifecycleListener,本质上还是一个List,具体实现过于细节,这里不做研究。同时LifecycleBase基于监听器的List,对添加监听器、移除监听器、获取所有监听器的方法实现了逻辑。

private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
//添加状态事件监听器的方法实现
@Override
public void addLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.add(listener);
}
//获取所有状态事件监听器的方法实现
@Override
public LifecycleListener[] findLifecycleListeners() {
    return lifecycleListeners.toArray(new LifecycleListener[0]);
}
//移除状态事件监听器的方法实现
@Override
public void removeLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.remove(listener);
}

需要特别注意的是,LifecycleBase实现了生命周期状态事件监听器处理事件的方法,其实就是遍历了监听器List,使用监听器的lifecleEvent方法处理了事件。这一方法在后面实现的init(),start()等生命周期的方法中都有间接调用,相当于只要更改了容器所处生命周期的状态,就需要对发布这一事件,调用该容器的关注该事件的监听器处理事件。

protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}
  • 生命周期方法实现

    LifecycleBase实现了Lifecycle的标准生命周期方法init()、start()、stop()、destroy()的逻辑。下面主要以init()和start()为例介绍实现的基本特点。

//init()生命周期方法实现
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
    //new状态是最初加载时必须为NEW
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);

        initInternal();        //实际生命周期实现逻辑,由子类实现

        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}

通过init()的具体代码可以发现,LifecycleBase中主要就是设置了生命周期的状态,其中setStateInternal()除了设置了状态、校验了状态是否正确之外,还调用了上文提到的fireLifecycleEvent()响应了当前状态事件,触发了监听该事件的监听器监听到该事件的逻辑。由于不同子类(主要是不同容器)在初始化时所需要做的事不一样,所以initInternal()是真正被子类重载后做初始化的事情的方法。
    start()内部逻辑的特点和init()很相似,也是做了设置生命周期的状态,稍微复杂一些是需要:1.判断状态是否处于可以start()的阶段,不然就需要先init();2.是否失败需要stop()。

@Override
public final synchronized void start() throws LifecycleException {
    //校验状态,是否已经启动
    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;  //已经启动,直接返回
    }

    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)) {
            stop(); //失败直接终止
        } else if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

可以看到,依旧有很多状态变更,和处理状态变更、发布状态变更事件的setStateInternal()的调用。

宏观来看各种容器生命周期的实际流程

    在原理(一)我已经详细介绍了各种容器的配置、静态嵌套结构和各自的功能,铺垫了这么多,终于可以介绍各种容器是如果加载初始化的了。

在这里插入图片描述

    首先按照基本嵌套结构,如图,最开始需要先启动Server,不管是Tomcat独立部署包还是spring mvc中的嵌入式tomcat-embed包,都需要先调用Server.init(),然后调用Server.start()。实际上,tomcat中Server的标准实现是StandardServer,根据LifecycleBase中实现的init()和start()的代码,实际上会调用子类重载的initInternal()和startInternal()方法。在StandardServer的initInternal()和startInternal()方法中,会调用StandardServer类中的Service实例的init()和start()方法.....另一个方面,在StandardServer的addService()方法也会调用添加的Service实例(实际是StandardService类)的start()方法。同样的逻辑适用于Service下的Engine容器。
    Engine以下的Host、Context、Wrapper略有不同,因为它们都实现自Container接口,继承了Container接口的标准实现ContainerBase。Container中将每一种容器的包含关系定义为父子关系,即Host是Engine类的Container child,使用这个Container[]来存储所有Host子容器。同理Host和Context、Context和Wrapper都是相同的关系。在ContainerBase中实现的startInternal()有:

Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
    results.add(startStopExecutor.submit(new StartChild(children[i])));
}

    需要特别注意的是,Container启动子容器的时候不一定是通过init()或者start()中调用相应子容器的生命周期函数。在容器的addChild方法中,也会调用子容器的start()方法,初始化加载和启动子容器,比如host的addChild(context)方法会调用context的start方法。
统一使用多线程执行调用child的start方法,这样各个容器都能按顺序初始化和启动。
    关于边界的情况,比如Wrapper部分,由于和spring mvc相关,会在spring mvc原理里面叙述。

本系列文章:
tomcat + spring mvc原理(一):tomcat原理综述和静态架构
tomcat + spring mvc原理(二):tomcat容器初始化加载和启动
tomcat + spring mvc原理(三):tomcat网络请求的监控与处理1
tomcat + spring mvc原理(四):tomcat网络请求的监控与处理2
tomcat + spring mvc原理(五):tomcat Filter组件实现原理
tomcat + spring mvc原理(六):tomcat WAR包的部署与加载
tomcat + spring mvc原理(七):spring mvc的Servlet和九大标准组件的静态结构与初始化
tomcat + spring mvc原理(八):spring mvc对请求的处理流程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容