Tomcat8源码笔记(五)组件Container分析

时间:2022-01-02 16:26:29

Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的?

说到Container的初始化,Tomcat8源码笔记(三)Catalina加载过程 这篇文章记录了Tomcat是怎样解析server.xml的流程,再此基础上,我们来分析Container的初始化。

这是Catalina定义的解析规则,之前都是添加的Rule接口的实现,而RuleSet可以理解为一堆Rule更具象的接口;而各种各样的Rule作用是啥,目的就是解析server.xml时候,按照规则来解析并且生成有结构的对象。

Tomcat8源码笔记(五)组件Container分析

Digester的addRuleSet方法:

因为Tomcat  server.xml没有用到DTD XSD文件声明元素,所以namespaceURI都是null(我是这样理解的),所以添加RuleSet类型的规则实际上是调用ruleSet的addRuleInstances方法.那就来记录下EngineRuleSet的addRuleInstances方法。

Tomcat8源码笔记(五)组件Container分析

Engine元素规则

这些规则ObjectCreate、SetProperties、SetNext分别有啥用,Tomcat8源码笔记(三)Catalina加载过程 这里就不多介绍了;

可以得出以下结论:Engine的实例化对象为StandardEngine,StandardEngine会有第一个监听器,EngineConfig,如果哪天不想添加EngineConfig作为第一个默认的监听器,方法也是可以的,修改Engine元素的engineConfigClass即可;   我们可给Engine元素添加这些子元素达到扩展的目的:Cluster、Listener、Valve、Realm,;

其实总结下来就是,Engine容器具体实例化对象为StandardEngine,且天生自带监听器EngineConfig,且Engine的父容器就是StandardService

Tomcat8源码笔记(五)组件Container分析

按照   Tomcat8源码笔记(三)Catalina加载过程 的说明,只是将StandardService调用了setContainer关联了StandardEngine,那是不是就像 Service是父母,而Engine是婴儿,父母知道婴儿是谁,但是婴儿却不知道父母是谁呢?  为了让Container容器知道父类Service是谁,实现方案也很简单:

Tomcat在给Service设置容器的时候,顺便双向关联了Engine,这里也能看出来,Tomcat结构中Service的容器就是Engine!

Tomcat8源码笔记(五)组件Container分析

其他属性先忽略,验证下我们得到的结论: 注意service以及lifecycleListener,可以验证我们上面的分析。

Tomcat8源码笔记(五)组件Container分析

StandardEngine容器初始化

按照之前 Tomcat8源码笔记(四)Server和Service初始化 分析的来看,StandardEngine初始化initInternal核心方法调用之前,触发监听器的before_init事件!

Tomcat8源码笔记(五)组件Container分析

当然了 StandardEngine目前的listener也就是EngineConfig,它触发时间仅仅针对 StandardEngine的start、stop方法才做逻辑处理,所以此处忽略其触发监听器before_init和after_init事件

Tomcat8源码笔记(五)组件Container分析

那StandardEngine初始化的真正方法initInternal做了什么处理?

getRealm方法只是为了确保realm属性的存在,哪怕没有Realm子元素,我也给你一个NullRealm;

super.initInternal在抽象类LifecycleMBeanBase的子类中,调用父类initInternal,是为了JMX注册,比如StandardEngine、StandardService都是为了JMX注册;

而到了容器这里,super.initInternal又进行了额外的处理,可以看成是容器特有的,和服务区分开来。

Tomcat8源码笔记(五)组件Container分析

IDEA给我们绘制的Tomcat组件结构中,可以看到LifecycleMBeanBase子类又抽象出ContainerBase,ContainerBase的initInternal方法又是一番逻辑!

Tomcat8源码笔记(五)组件Container分析

ContainerBase的initInternal

可以看到初始化了一个ThreadPoolExecutor,corePoolSize和maxPoolSize默认都是1,线程名字前缀为getName()+”-startStop-”,比如StandardEngine,线程名字就是

Catalina-startStop-1这种模式。 之后才是用父类LifecycleMBeanBase的initInternal注册JMX中.

Tomcat8源码笔记(五)组件Container分析

继续回到StandardService的初始化,Tomcat8源码笔记(四)Server和Service初始化  container已经初始化完毕,而executor、mapperListener的初始化工作除了JMX之外,没任何操作了,这两个组件真正起作用是在start启动中;后面文章就以Tomcat核心组件Connector初始化来分析。

Tomcat8源码笔记(五)组件Container分析

补充于2019.4.9

StandardEngine的实例化,这么重要的居然忘记记录了!

Tomcat中容器的实例化基本都是通过空参构造器反射实例化,暂时未见到例外;查看StandardEngine的空参构造器

Tomcat8源码笔记(五)组件Container分析

这里就不得不记录一个重要的接口Pipeline的分析

Pipeline接口所有方法围绕着Valve和Container有关,

Tomcat8源码笔记(五)组件Container分析

Valve接口

Valve接口有点像链表,我可以获取我下一个指向的链表; Valve接口还定义了invoke方法; 按照英语翻译Valve是阀门的意思,而Pipeline是管道的意思;

Tomcat8源码笔记(五)组件Container分析

上面接口介绍有点抽象,直接来看下StandardEngine中的管道和阀门吧; 首先pipeline的初始化不是在StandardEngine中,而是在ContainerBase中:实例化Pipeline实现类,并且调用setContainer将容器和Pipeline关联在一起。

Tomcat8源码笔记(五)组件Container分析

而StandardEngine初始化过程中就使用了pipeline.setBasic方法,我们来看下setBasic方法:

setBasic方法设置了pipeline的basic,并且如果原来pipeline存在first,first下一个指向原来的basic,那现在basic是新添加的valve,且原来的basic的next就是添加进来的valve.

Tomcat8源码笔记(五)组件Container分析