IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 拆解Tomcat10: (五) 核心组件的协调控制与设计模式解析 -> 正文阅读

[Java知识库]拆解Tomcat10: (五) 核心组件的协调控制与设计模式解析

系列文章目录

拆解Tomcat10: (一) 如何快速的学习一门新的语言
拆解Tomcat10: (二) 在Idea中调试最新的Tomcat10源码
拆解Tomcat10: (三) 图解Tomcat的启动过程
拆解Tomcat10: (四) 图解架构
拆解Tomcat10: (五) 核心组件的初始化与设计模式解析 - - 本文



前言

上一篇《拆解Tomcat10: (四) 图解架构》分享了Tomcat的核心组件及其架构关系,本章接着讲解组件的Tomcat的初始化过程,这么多组件是如何统一加载、启动的。这其中用到了哪些设计模式和设计原则。


一、Server的初始化

Catalina.createStartDigester()方法中指定了由哪些实现类去实现对应的接口,那么这些组件是如何实现Lifeycle接口的?

1. 总览生命周期接口的实现方式

下图在上一篇图二的基础上补充了实现逻辑,见下图中的紫色部分(仅用于展示结构关系,未画所有Lifeycle相关组件):


(图一)

在Lifeycle接口中,定义了初始化(init)、启动(start)、停止(stop)、销毁(destory)、获取当前状态(getState)等方法,从Lifeycle的名字也可以知道,这个接口用于定义对象的生命周期,即生老病死的过程。

public interface Lifecycle {
    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();
}

2. 通用抽象类

直接实现Lifeycle接口的是LifecycleBase类,这是一个抽象类。以其实现init()方法为例:

    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.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());
        }
    }


    /**
     * 子类实现此方法以执行所需的任何实例初始化。
     *
     * @throws LifecycleException If the initialisation fails
     */
    protected abstract void initInternal() throws LifecycleException;

在LifecycleBase类中有两个对应的init相关方法,首先init()方法Override父类的方法,通过setStateInternal方法触发相应状态的事件(具体后文描述,不是此处重点),然后调用另一个抽象方法initInternal()。

3. 子类的实现逻辑

这里预留的initInternal()方法是做什么用的呢,看一下StandardServer类中对此方法的具体实现:

    @Override
    protected void initInternal() throws LifecycleException {
    	// 执行父级的逻辑
        super.initInternal();

        // 初始化 utility executor
        reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
        register(utilityExecutor, "type=UtilityExecutor");

        // 注册全局字符串缓存注意虽然缓存是全局的,但如果JVM中存在多个服务器(嵌入时可能会发生),那么相同的缓存将以多个名称注册
        onameStringCache = register(new StringCache(), "type=StringCache");

        // 注册 MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");

        // 注册并初始化 naming resources
        globalNamingResources.init();

        //此处省略了加载器相关的代码
        
        
        // 初始化定义的 Services
        for (Service service : services) {
            service.init();
        }
    }

可以看到这里是具体Server相关的代码,也就是说,initInternal()是预留给子类实现的,由子类通过重写此方法来实现自己的个性逻辑。

为什么要这样设计呢?这就是模板方法模式。

二、☆模板方法模式(TEMPLATE METHOD)

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

– 《设计模式:可复用面向对象软件的基础》

结合上一节的例子:

  • 由LifecycleBase定义了一个算法骨架,来实现Lifecycle接口的init()方法。这个算法骨架就是模板方法。
  • LifecycleBase类是一个通用的类,所以其中的逻辑只能是通用的逻辑。这些逻辑写在init()方法中,即通过setStateInternal方法触发相应状态的事件等功能逻辑。
  • 调用一个抽象方法initInternal(),这个方法交由子类去重写,来实现具体业务逻辑。

1. 总结一下模板方法模式适用的场景:

  • 一个算法逻辑由多个类实现,不同的类之间的逻辑有通用的也有个性化的。
  • 创建一个抽象类,完成算法骨架。
  • 抽象类中实现通用逻辑,并调用一个空方法,具体逻辑交由子类实现。
  • 继承抽象类的子类实现自身的个性化逻辑。

2. 模板方法模式的好处:

  • 代码复用,子类不用再写一遍通用的逻辑。
  • 定义了算法骨架,对算法实现进行了约束。
  • 权限隔离,抽象类和子类可能由不同角色完成,子类的修改不会影响通用逻辑,也就不会影响其他子类。

类图如下:

image-20220110211556780
(图二)(引自《设计模式:可复用面向对象软件的基础》)

这也是非常惯例的一种实现方式,如果一个接口会被多个不同类实现,那么常见的操作就是使用一个类去实现这个接口,在这个实现类中编写通用的方法,并调用需要子类实现的抽象方法。子类直接或间接继承这个抽象类,并根据自身需要实现具体逻辑。

三、所有核心组件的Init方法传递

继续上一篇的Catalina类的load()方法,此时完成了对Server.xml文件的解析,并将其赋值给了Catalina的server属性。接下来就是调用getServer().init();方法进行初始化。Server组件作为最上层组件,我们已经知道了其初始化是如何进行的,那么其他子组件是如何统一 管理的呢?

看一下第一节中StandardServer类的initInternal()方法的代码,在最后一部分通过循环遍历的方式调用了所有Service的init方法。

// 初始化定义的 Services
for (Service service : services) {
    service.init();
}

同理,由图一可知,Service同样是继承了LifecycleBase类,所以Service和Server的上层通用逻辑是一样的,那么看一下StandardService的initInternal()方法:

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //Engine 初始化
    if (engine != null) {
        engine.init();
    }

    // 初始化 Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // 初始化 mapper listener
    mapperListener.init();

    // 初始化 our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();
        }
    }
}

其中进行了Engine、Executors、mapperListener、Connectors的初始化,以此类推,其他的组件的initInternal()方法我们就不逐一研究了,因为此处暂不关心这几个组件的功能,仅关注组件之间的关系和调用逻辑。具体每个组件的功能及处理逻辑在单独学习该组件的时候再进行分析。

大概流程如下(只画了部分组件):

tomcat-times
(图三)

四、 总体的调用流程

通过init方法的例子,我们可以大概明白Tomcat的这些核心组件之间的初始化流程。其实对于Lifecycle接口的其他生命周期方法也是类似的,启动(start)、停止(stop)、销毁(destory)等方法也是这样从根节点逐级传递到叶子节点的。也同样存在对应的startInternal()、stopInternal()、destroyInternal()方法。

当然,并不是每个组件都会重写这些实际的生命周期方法 XXXInternal(), 比如 StandardHost就没有重写 initInternal()方法,但重写了startInternal()方法。 这完全由按照逻辑需要决定,反过来说,这也给后期逻辑扩展预留了位置。

所以,对于Tomcat的启动停止等操作,实际上逻辑是这样的:

image-20220110223242228
(图四)

五、 Bootstrap、Catalina与组件的生命周期方法的对应关系

用户对Tomcat发出Start、Stop等指令,Tomcat的Bootstrap中的main方法接到对应的指令参数后,通过反射方式调用Catalina的对应方法,再由Catalina调用Server的对应方法。至此,这个核心组件树由根部开始逐步调用下一级的对应方法,直至叶子节点。

用户指令参数、Catalina的方法以及核心组件的生命周期方法的对应关系如下图:

image-20220110222720011
(图五)


总结

通过本文我们总览了核心组件之间的关系以及逐级调用的逻辑。也以此为例学习了一个设计模式:模板方法模式。
思考:设计模式之间经常关联使用,在这个例子里,还用到了什么设计模式或者原则呢?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-14 01:50:01  更:2022-01-14 01:50:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:40:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码