tomcat lifecyclelistener_继续,来聊聊Tomcat的容器

f84623bf5b599b2d6acdeb1db82907a4.png

作者:不学无数的程序员

链接:https://urlify.cn/jYZFFf

在这篇文章《Tomcat是如何运行的?整体架构又是怎样的?》中我们简单介绍了容器的概念,并且说了在容器中所有子容器的父接口是Container。在死磕Tomcat系列(2)——EndPoint源码解析中,我们知道了连接器将请求过来的数据解析成Tomcat需要的ServletRequest对象给容器。那么容器又是如何将这个对象准确的分到到对应的请求上去的呢?

# 容器的整体设计

Container是容器的父接口,所有子容器都需要实现此接口,我们首先看一下Container接口的设计。

public interface Container extends Lifecycle {    public void setName(String name);    public Container getParent();    public void setParent(Container container);    public void addChild(Container child);    public void removeChild(Container child);    public Container findChild(String name);}

Tomcat是如何管理这些容器的呢?我们可以通过接口的设计可以了解到是通过设置父子关系,形成一个树形的结构(一父多子)、链式结构(一父一子)来管理的。一想到树形的结构我们应该就立马能够联想到设计模式中的组合模式,而链式结构我们应该能够想到设计模式中的责任链设计模式。无论这两种的哪一种我们都知道这种关系是上下层级的关系。用图来表示就是如下。

2c62409cddb8dbcbf8f61477907b9647.png

既然是父子的结构,那么连接器是如何将转换好的ServletRequest给到容器的呢?我们可以看CoyoteAdapter中的service方法。因为在连接器中最后一环是将解析过的Request给到Adapter运用适配器设计模式解析为ServletRequest对象。在service方法中我们看到有这么一句。

connector.getService().getContainer().getPipeline().getFirst().invoke(                        request, response);

而其中的getContainer方法,返回的是Engine对象

public Engine getContainer();

这里看到了Pipeline,Pipeline应该大家有所熟悉,是管道的概念,那么管道里面装的是什么呢?我们看其定义的方法

public interface Pipeline extends Contained {  public void addValve(Valve valve);  public Valve getBasic();  public void setBasic(Valve valve);  public Valve getFirst();}

可以看到Pipeline管道里面装的是Valve,那么Valve是如何组织起来的呢?我们也可以看它的代码定义

public interface Valve {  public Valve getNext();  public void setNext(Valve valve);  public void invoke(Request request, Response response)}

可以知道每个Valve都是一个处理点,它的invoke就是相对应的处理逻辑。可以看到有setNext的方法,因此我们大概能够猜到是通过链表将Valve组织起来的。然后将此Valve装入Pipeline中。因此每个容器都有一个Pipeline,里面装入系统定义或者自定义的一些拦截节点来做一些相应的处理。因此只要获得了容器中Pipeline管道中的第一个Valve对象,那么后面一系列链条都会执行到。

但是不同容器之间Pipeline之间是如何进行触发的呢?即例如Engine的Pipeline处理完了最后一个Valve,那么如何调用Host的PipeLine管道中的Valve呢?我们可以看到每个Pipeline中还有一个方法。setBasic这个方法设置的就是Valve链条的末端节点是什么,它负责调用底层容器的Pipeline第一个Valve节点。用图表示就是这样的。

add29b12cbdb9894ce5c6f61be97da68.png

# Engine容器

Engine容器比较简单,只是定义了一些基本的关联关系。它的实现类是StandardEngine。

    @Override    public void addChild(Container child) {        if (!(child instanceof Host))            throw new IllegalArgumentException                (sm.getString("standardEngine.notHost"));        super.addChild(child);    }    @Override    public void setParent(Container container) {        throw new IllegalArgumentException            (sm.getString("standardEngine.notParent"));    }

需要注意Engine容器是没有父容器的。如果添加是会报错。添加子容器也只是能添加Host容器。

# Host 容器

Host容器是Engine的子容器,一个Host在Engine中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这个应用,并且标识这个应用以便能够区分它们。它的子容器通常是Context容器。我们可以看配置文件中也能够看出Host文件的作用。

            unpackWARs="true" autoDeploy="true">

那么Host容器在启动时具体干了什么呢?我们看它的startInternal方法看不出来什么,只是启动了相应的Valve,是因为在Tomcat的设计中引入了生命周期的概念,即每个模块都有自己相应的生命周期,模块的生命周期定义有NEW、INITIALIZING、INITIALIZED、SSTARTING_PREP、STARTING、STARTED,每个模块状态的变化都会引发一系列的动作,那么这些动作的执行是直接写在startInternal中吗?这样会违反开闭原则,那么如何解决这个问题呢?开闭原则说的是为了扩展性系统的功能,你不能修改系统中现有的类,但是你可以定义新的类。

于是每个模块状态的变化相当于一个事件的发生,而事件是有相应的监听器的。在监听器中实现具体的逻辑,监听器也可以方便的增加和删除。这就是典型的观察者模式。

那么Host容器在启动的时候需要扫描webapps目录下面的所有Web应用,创建相应的Context容器。那么Host的监听器就是HostConfig,它实现了LifecycleListener接口

public interface LifecycleListener {    public void lifecycleEvent(LifecycleEvent event);}

接口中只定义了一个方法,即监听到相应事件的处理逻辑。可以看到在setState方法中调用了监听器的触发。

protected void fireLifecycleEvent(String type, Object data) {    LifecycleEvent event = new LifecycleEvent(this, type, data);    for (LifecycleListener listener : lifecycleListeners) {        listener.lifecycleEvent(event);    }}

所以容器中各组件的具体处理逻辑是在监听器中实现的。

# Context 容器

一个Context对应一个Web应用

Context代表的是Servlet的Context,它具备了Servlet的运行的基本环境。Context最重要的功能就是管理它里面的Servlet实例,Servlet实例在Context中是以Wrapper出现的。Context准备运行环境是在ContextConfig中lifecycleEvent方法准备的。

@Overridepublic void lifecycleEvent(LifecycleEvent event) {    // Identify the context we are associated with    try {        context = (Context) event.getLifecycle();    } catch (ClassCastException e) {        log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);        return;    }    // Process the event that has occurred    if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {        configureStart();    } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {        beforeStart();    } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {        // Restore docBase for management tools        if (originalDocBase != null) {            context.setDocBase(originalDocBase);        }    } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {        configureStop();    } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {        init();    } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {        destroy();    }}

# Wrapper容器

Wrapper容器代表一个Servlet,包括Servlet的装载、初始化、执行以及资源的回收。Wrapper是最底层的容器,它没有子容器。

Wrapper的实现类是StandardWrapper,主要任务是载入Servlet类,并进行实例化。但是StandardWrapper类并不会调用Servlet的service方法。而是StandardWrapperValue类通过调用StandardWrpper的allocate方法获得相应的servlet,然后通过拦截器的过滤之后才会调用相应的Servlet的service方法

# 总结

Tomcat的容器中有许多值得我们学习的设计思想,例如将不变的抽取出来,然后变化的子类来实现的模板设计模式、维护一堆父子关系的组合设计模式、事件的发生伴随监听者的相应动作执行的观察者设计模式等等。

在学习框架的时候,有时没必要深究里面一行一行的代码,而要学习它的思想。知道它是如何运行,随后如果查找问题,或者是对框架进行相应扩展。这时候再深入学习里面的代码将会事半功倍。

 往期推荐 

?

  • 转给你的老板,如何给程序员更少的薪水?
  • 20年3月数据库排行榜
  • v2ex:飞机上用的是什么操作系统?

03c7eeb1427bb9d8c7d96568d6541222.png

ea242edabff464ebd2136312c0437bb3.png

点击

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/488610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何与病毒搏斗?这部BBC“史诗级大片”告诉你答案

来源:惠在湖北 我们知道,在目前没有特效药和疫苗的情况下,被治愈的新型冠状病毒肺炎患者,离不开医学的帮助,而治愈的关键,依靠的是人体自身的免疫力。病毒是如何入侵的?免疫力从何而来&#xff…

markdown 行内公式_使用Markdown快速编辑公众号技巧之mdnice

请使用 Chrome 浏览器。请阅读下方文本熟悉工具使用方法,本文可直接拷贝到微信中预览。1 Markdown Nice 简介支持自定义样式的 Markdown 编辑器支持微信公众号、知乎和稀土掘金欢迎扫码回复「排版」加入用户群2 主题https://preview.mdnice.com/themes/欢迎提交主题…

AI人必看!89页全网最全清华知识图谱报告(附PDF)

来源:智东西知识图谱(Knowledge Graph)是人工智能的重要分支技术,它在2012年由谷歌提出,成为建立大规模知识的杀手锏应用,在搜索、自然语言处理、智能助手、电子商务等领域发挥着重要作用。知识图谱与大数据…

AI战“疫”!人工智能在疫情中的重要作用

来源:腾讯新闻网新冠肺炎疫情牵动着全国人民的心!在防控疫情部署落实工作中,上海着力将人工智能等现代信息技术深入应用于疫情态势研判、传播路径分析、精准防控、有效治疗及后续治理等各工作环节。通过提供更加精准有效的科学决策依据&#…

linux 判断指针是否可读_Linux进程间通信——消息队列

概念什么是消息队列?消息队列亦称报文队列,也叫做信箱。是Linux的一种通信机制,这种通信机制传递的数据具有某种结构,而不是简单的字节流。消息队列的本质其实是一个内核提供的链表,内核基于这个链表,实现了…

【人工智能】中科院2019全球人工智能发展白皮书!八大关键技术中国崛起中,国产AI芯片耀眼...

来源:产业智能官人工智能是一个很宽泛的概念,概括而言是对人的意识和思维过程的模拟,利用机器学习和数据分析方法赋予机器类人的能力。人工智能将提升社会劳动生产率,特别是在有效降低劳动成本、优化产品和服务、创造新市场和就业…

斜度符号标注_几何作图之斜度和锥度

虽然机件的形状各有不同,但它们的图样基本上都是由直线、圆弧和其它一些曲线所组成的几何形体。因此在绘图时,常常要运用一些几何作图方法。斜度和锥度1.斜度(GB/T 4458.4-2003)斜度是指一直线(或平面)对另一条直线(或平面)的倾斜程度&#x…

shiro 手动创建session_Shiro 和 spring boot 的集成

1 添加依赖使用 shiro-spring-boot-web-starter 在 spring boot 中集成 shiro 只需要再添加一个依赖<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.4.1</version…

动态规划 所有题型的总结

1 动态规划 1.1 定义 动态规划的核心是状态和状态转移方程。 在记忆化搜索中&#xff0c;可以为正在处理的表项声明一个引用&#xff0c;简化对它的读写操作&#xff1b; 动态规划解决的是多阶段决策问题&#xff1b; 初始状态→│决策&#xff11;│→│决策&#xff12;│→……

美智库:马赛克战是人工智能与自主系统支撑的决策中心战

来源&#xff1a;国防科技要闻编者按2020年2月11日&#xff0c;美战略与预算评估中心发布报告《马赛克战&#xff1a;利用人工智能和自主系统实施决策中心战》。针对大国战略竞争&#xff0c;报告建议美国防部摒弃当前以消耗战为中心的理念&#xff0c;采用决策中心战。报告分析…

echarts山东地图_用 Python 绘制个人足迹地图

前两年&#xff0c;足迹地图小程序风靡朋友圈&#xff0c;一时间大家都流行晒自己的旅行地图。但是&#xff0c;笔者最近体验了好几款足迹地图的小程序&#xff0c;发现这些小程序虽然号称是足迹地图&#xff0c;但最多只是展示到省级别&#xff0c;无法精确到市级别&#xff0…

AI 芯片发展的前世今生

来源&#xff1a;《微纳电子与智能制造》期刊作者&#xff1a;任 源、潘 俊、刘京京、何燕冬、何 进现代电子产品和设备在诸如通信 、娱乐 、安全和医疗保健等许多方面改善了我们的生活质量 &#xff0c;这主要是因为现代微电子技术的发展极大地改变了人们的日常工作和互动方式…

用户解锁不存在_苹果推送iOS 13.5测试版,戴口罩解锁更顺畅

目前最新的 iOS 系统正式版为 13.4.1&#xff0c;之前苹果已经开始 iOS 13.4.5 的测试工作&#xff0c;并于 4 月 16 日推送了 Beta2 版本。令人意外的是&#xff0c;今日凌晨苹果为参与测试的设备直接推送了 iOS 13.5 的测试版更新&#xff0c;而且是 Beta3 版本(「iOS 13.5 B…

shell for循环1到100_浅谈Linux下shell 编程的for循环常用的6种结构

浅谈Linux下shell 编程的for循环常用的6种结构1、 常用for循环结构(1)for 变量 in 值1 值2 值3...do程序块儿done(2)for 变量 in 命令 (或者$())do程序块儿done(3)for 变量 in {1..100}do程序块儿done(4)for 变量 in "$"do程序块儿done(5)for 变量 in /etc/*.confdo程…

案例163:基于微信小程序的校园二手交易平台系统设计与开发

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

密歇根大学28页最新《GANs生成式对抗网络综述:算法、理论与应用》最新论文,带你全面了解GAN技术趋势...

来源&#xff1a;专知【导读】生成式对抗网络&#xff08;Generative Adversarial Networks&#xff0c;GANs&#xff09;作为近年来的研究热点之一&#xff0c;受到了广泛关注&#xff0c;每年在机器学习、计算机视觉、自然语言处理、语音识别等上大量相关论文发表。密歇根大学…

201521123035《Java程序设计》第八周学习总结

1. 本周学习总结 1.1 以你喜欢的方式&#xff08;思维导图或其他&#xff09;归纳总结集合与泛型相关内容。 1.2 选做&#xff1a;收集你认为有用的代码片段 //泛型方法&#xff0c;打印MyStack的所有元素的薪水&#xff0c;不管MyStack中的元素是Employee类型或者Employee的子…

x79主板bios设置详解_BIOS(主板)常用功能:设置启动磁盘

BIOS是什么&#xff1f;BIOS是英文"Basic Input Output System"的缩写&#xff0c;翻译成中文名称就是"基本输入输出系统"。BIOS是个人电脑启动时加载的第一个软件&#xff0c;是连接电脑硬件和软件的枢纽&#xff0c;有多重要就不言而喻了吧&#xff01;B…

《自然-神经科学》发表脑智卓越中心关于昼夜节律中枢的研究成果

来源&#xff1a;脑智卓越中心昼夜节律在生物体中广泛存在&#xff0c;对调节人们一天之中的运动、睡眠、代谢等诸多生理过程起着重要的作用。目前认为哺乳动物的昼夜节律是由位于大脑中的视交叉上核&#xff08;Suprachiasmatic Nucleus, SCN&#xff09;控制的。SCN能够接收视…

mybatis 打印sql_mybatis-plus 3.1.0 发布,划重点一大波升级

mybatis-plus 是一款 Mybatis 动态 SQL 自动注入 Mybatis 增删改查 CRUD 操作中间件&#xff0c; 减少你的开发周期优化动态维护 XML 实体字段&#xff0c;无入侵全方位 ORM 辅助层让您拥有更多时间吃鸡、陪家人、陪朋友。更新日志升级 mybatis 到 3.5.0 版本升级 mybatis-spri…