死磕Tomcat系列(5)——容器

回顾

在死磕Tomcat系列(1)——整体架构中我们简单介绍了容器的概念,并且说了在容器中所有子容器的父接口是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是如何管理这些容器的呢?我们可以通过接口的设计可以了解到是通过设置父子关系,形成一个树形的结构(一父多子)、链式结构(一父一子)来管理的。一想到树形的结构我们应该就立马能够联想到设计模式中的组合模式,而链式结构我们应该能够想到设计模式中的责任链设计模式。无论这两种的哪一种我们都知道这种关系是上下层级的关系。用图来表示就是如下。

既然是父子的结构,那么连接器是如何将转换好的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节点。用图表示就是这样的。

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文件的作用。

那么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

tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器相关推荐

  1. 大公司程序员 VS 小公司程序员 | 差别在哪?

    很多大学生毕业之后,都希望能进互联网大厂,但是大厂并没有那么好进,不说别的有的学历就会把你卡在门外.但是大厂有大厂的优势,小公司有小公司的优势.根据自己的需求选择自己合适的公司,才是最重要的. 大公司 ...

  2. 一个大公司程序员的牢骚

    在中国,如果 40 岁了还在写代码,是一种幸福,还是一种悲哀? 一个大公司程序员的牢骚 从360来百度已经一年了,期间经历过很多的需求开发,也带领小伙伴做过Android插件方向的研发与通宵上线.没有 ...

  3. 死磕Tomcat系列(6)——Tomcat如何做到热加载和热部署的

    死磕Tomcat系列(6)--Tomcat如何做到热加载和热部署的 热部署就是在服务器运行时重新部署项目,热加载即在在运行时重新加载class,从而升级应用. 通常情况下在开发环境中我们使用的是热加载 ...

  4. 什么样的公司程序员待遇好

    在什么样的公司程序员待遇好? 这个问题的答案见仁见智.但是,如果换成:"请举例说明程序员待遇好的公司",一定会有许多朋友大声喊出来--Google.微软--或许还有其他一些国内.外 ...

  5. 什么样的公司程序员待遇好?

    在什么样的公司程序员待遇好? 这个问题的答案见仁见智.但是,如果换成:"请举例说明程序员待遇好的公司",一定会有许多朋友大声喊出来--Google.微软--或许还有其他一些国内.外 ...

  6. html简单个人网页制作 HTML5+CSS大作业——程序员个人简历设计(5页)

    HTML5+CSS大作业--程序员个人简历设计(5页) 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. 酒店. 舞蹈. 动漫. 明星. 服装. ...

  7. python入门教程软件-程序员带你十天快速入门Python,玩转电脑软件开发(四)

    本系列文章立志于从一个已经习得一门编程语言的基础之上,全面介绍Python的相关开发过程和相关经验总结.本篇文章主要是基于上一篇的程序员带你十天快速入门Python,玩转电脑软件开发(三)的基础之上, ...

  8. 程序员转正述职报告_公司程序员试用期转正工作总结

    公司程序员试用期转正工作总结(一) 来公司担任程序员一职已x个月时间,在这x个月时间里,我学到了很多东西.每个人都是在不断的总结中成长,在不断的审视中完善自己.在这x个月里自己也是在总结.审视中脚踏实 ...

  9. 什么是整洁代码?大咖程序员们这样说

    这是本有关编写好程序的书.它充斥着代码.我们要从各个方向来考察这些代码.从顶向下,从底往上,从里而外.读完后,就能知道许多关于代码的事了. <代码整洁之道> 马丁 著 而且,我们还能说出好 ...

最新文章

  1. 简析平衡树(三)——浅谈Splay
  2. idea工具使用总结
  3. Swift:print()vs println()vs NSLog()
  4. Dwg图纸属性的读取
  5. golang float浮点型精度丢失问题解决办法:使用decimal包;float与int的相互转换
  6. RISC-V,芯片中的网红战斗机,究竟是个什么鬼
  7. 几个有趣的python技巧
  8. Socket编程 涵盖代码和函数参数介绍
  9. 斯坦福大学NLP公开课CS224n上映啦!华人助教陪你追剧
  10. python标准库sys模块常用函数
  11. 深度学习实现工业零件的缺陷检测
  12. EXCEL-日常技巧整理-2-单元格拆分后原数据填充
  13. C语言填空概念题及答案,C语言填空题以及答案
  14. 根据excel的链接下载到电脑上
  15. 如何做好新媒体运营推广工作,黎想首谈一位优秀新媒体运营策划人员的思维模型
  16. 化工厂人员定位的实施,新导化工厂人员定位带来的效果
  17. 微信公众号网页授权,获取用户信息以及openid -- PHP后台
  18. 2019年英语专升本英语阅读「Part II 阅读专区」【文章(图片)、答案、词汇记忆】
  19. HCIP-IoT MQTT协议技术原理
  20. 数据结构 | 第十一章:二叉树和其他树 | 【前序遍历】【中序遍历】【后序遍历】【层次遍历】 | 并查集

热门文章

  1. NoSQL和传统数据库的区别
  2. MySql数据类型介绍
  3. textcnn文本词向量_文本分类模型之TextCNN
  4. 博弈论 —— matlab
  5. c语言5的阶乘流程图_5 种前途迷茫的编程语言
  6. 山东工业大学计算机及应用,彭玉旭副教授
  7. 乒乓球十一分制比赛规则_乒乓球竞赛规则 赛制和比赛规则
  8. 两步路轨迹文件位置_最新Uber ATG的轨迹预测方法LiRaNet介绍
  9. python保存图片到指定路径_使用Python将不同大小照片制作为GIF动画2
  10. mysql索引创建及使用注意事项