标题和上一篇很像,所以特别强调一下,这个是Tomcat对象的。

  从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start、before_init、after_start的lifecycleEvent事件给相应的监听器(如果有的话)。进入start,因为此时状态是LifecycleState.NEW,所以会执行init方法:

    public final synchronized void init() throws LifecycleException {
if(!this.state.equals(LifecycleState.NEW)) {
this.invalidTransition("before_init");
} try {
this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);
this.initInternal();
this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);
} catch (Throwable var2) {
ExceptionUtils.handleThrowable(var2);
this.setStateInternal(LifecycleState.FAILED, (Object)null, false);
throw new LifecycleException(sm.getString("lifecycleBase.initFail", new Object[]{this.toString()}), var2);
}
}

  首先,状态变为LifecycleState.INITIALIZING并发布一个before_init的LifecycleEvent给所有lifecycleListeners:

  这里的super:

  因为上面是server.start调用的start方法,所以虽然方法的代码在LifecycleBase中,但this指的是StandardServer的实例,于是这里的this.initInternal走的是StandardServer的initInternal方法,initInternal首先调用了super.initInternal,这里的super是LifecycleMBeanBase,MBean是用于JMX的能代表管理资源的管理构件,JMX定义了四种管理构件:标准、动态、开放和模型管理构件。每一种管理构件可以根据不同的环境需要进行制定,检查标准管理构件接口和应用设计模式的过程被称为内省(Introspection),动态管理构件提供了更大的灵活性,它可以在运行期暴露自己的管理接口。它的实现是通过实现一个特定的接口DynamicMBean。MBeanFactoryInitializer初始化是在BackgroundPreinitializer的onApplicationEvent。MBean功能相当强大,例如可以提供服务器的远程管理,当然也可以自定义此类功能: 

  onameStringCache = register(new StringCache(), "type=StringCache")注册全局字符串缓存,这里是使用DynamicMBean的方式,注册后StringCache也提供了类似上图的被管理功能,可以远程清楚服务器的字符串缓存等。 下一句globalNamingResources.init()同样的LifecycleBase的init套路,先是setStateInternal更新globalNamingResources的LifecycleState状态为INITIALIZING发布before_init事件,然后NamingResourcesImpl的initInternal,里面依然是之前的super.initInternal(),显示注册ContextResource、ContextEnvironment、ContextResourceLink避免注册时序问题,重复注册没关系;又是一个globalNamingResources的LifecycleBase的setStateInternal方法,更新LifecycleState状态为INITIALIZED发布after_init事件;然后回到StandardServer的initInternal,循环init之前add给server的service:

  又是的LifecycleBase的init,只不过这次是StandardService[Tomcat],更新的LifecycleState状态为INITIALIZING发布before_init事件,StandardService的initInternal,super之后是engine.init,同样engine现在也是初始化阶段,更新状态发布事件,然后进入StandardEngine的initInternal:

    protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}

  Realm是关于权限的,具体可以看http://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html;super(ContainerBase).initInternal创建了一个线程池startStopExecutor,这个startStopExecutor之后会接受两种任务StartChild和StopChild用池线程启动和停止子容器,StartStopThreadFactory会在创建线程时将线程设为守护线程,线程名例如:Thread [Tomcat(此处是容器的name)-startStop-1,5,main]。之后setStateInternal更新engine的LifecycleState状态为INITIALIZED发布after_init事件。如果service之前add过Executor,会将这些Executor初始化,如果Executor是JmxEnabled则设置作用范围。mapperListener的初始化没有特殊逻辑,就是先改状态为正在初始化并发布初始化之前的事件,然后注册MBeanServer,再改状态为初始化完成并发布初始化后事件。然后是在同步代码块中初始化Connector,不过之前已经将Connector与Service解绑了,所以这里什么都没做。于是,Service的初始化完成了,更新service的LifecycleState状态为INITIALIZED发布after_init事件。接着Server的初始化也完成了,同样也是更新状态发布事件。回到Server的start(虽然代码在Lifecycle中),setStateInternal(LifecycleState.STARTING_PREP, null, false)更新LifecycleState状态为准备启动,发布before_start事件;startInternal首先发布一个configure_start事件,接着setState(LifecycleState.STARTING)就将状态改为了STARTING同时发布start事件;globalNamingResources.start()更新状态setStateInternal(LifecycleState.STARTING_PREP, null, false)发布before_start事件;globalNamingResources的startInternal方法,发布configure_start事件并setState(LifecycleState.STARTING),globalNamingResources启动完成改状态STARTED发布after_start事件;然后回到server代码中,在同步代码块中启动service:

        // Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}

  当前状态的Service会执行setStateInternal(LifecycleState.STARTING_PREP, null, false),然后到StandardService的startInternal方法,setState(LifecycleState.STARTING)不说了,接着是同步代码块中engine.start(),里面是engine状态变更setStateInternal(LifecycleState.STARTING_PREP, null, false),startInternal中super.startInternal执行ContainerBase的对应方法,初始化logger;然后((Lifecycle) realm).start(),start方法里又是一个循环,从NEW到INITIALIZING的状态变化,然后进入RealmBase的initInternal方法

super.initInternal中MBeanServer,然后this.containerLog = container.getLogger(),此处container是StandardEngine[Tomcat],x509UsernameRetriever = createUsernameRetriever(x509UsernameRetrieverClassName),参考:https://bz.apache.org/bugzilla/show_bug.cgi?id=52500;状态连续更新到INITIALIZED然后STARTING_PREP,发布的什么事件我就不写了,现在还在Realm[Simple]中,接着是RealmBase的startInternal方法,它初始化了credentialHandler = new MessageDigestCredentialHandler()并将状态由改为了STARTING发布start事件,接着又改状态了STARTED事件after_start;然后回到了StandardEngine [Tomcat]中,通过ContainerBase的findChildren方法找到了子容器:

  随后将host的启动,提交给了之前初始化的startStopExecutor:

        for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}

  借助Future<Void>获取线程执行的返回值;

  下面就到了执行注册到engine的pipeline中的Value对象了((Lifecycle) pipeline).start(),StandardPipeline整套的状态变化事件发布我就不写了,initInternal方法是空实现,需要说的只有pipeline的startInternal方法,会取第一个Value对象,如果没有会执行basic(StandardEngineValve[Tomcat]实例),最后会将pipeline中的Value对象依次start:

        Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}

  StandardEngineValve的整套start不说了,其中initInternal只有super.initInternal的MBeanServer和初始化containerLog,startInternal就只有改变状态也不说了,出来后是pipeline的状态变化,这个方法状态都是变为STARTING标配也没啥好说的,STARTED然后回到engine,StandardEngine[Tomcat]变状态为STARTING,之后是threadStart(),代码在ContainerBase中,启动了背景线程:

  上面configureEngine中配置的engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay)指定背景线程的执行间隔Thread.sleep((long) ContainerBase.this. backgroundProcessorDelay * 1000L),背景线程会处理例如StandardContext中e.getLoginConfig() == null时e.getPipeline().addValve(new NonLoginAuthenticator()),这个value包含session管理的相关逻辑,例如背景线程会在每隔多长时间后判断session是否失效之类。终于StandardEngine实例也到了STARTED状态,该回到StandardService中了,前面说了我这并木有executor,所以没执行:

        synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}

  然后是start前面初始化过的mapperListener:

        setState(LifecycleState.STARTING);

        Engine engine = service.getContainer();
if (engine == null) {
return;
} findDefaultHost(); addListeners(engine); Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}

  上面代码中给出的engine就是StandardEngine[Tomcat],findDefaultHost名字说的很清楚,我这里找出了localhost并set给MapperListener的mapper( mapper. setDefaultHostName ),addListeners本身是个递归,会将this(MapperListener)add给各个child容器(比如StandardContext、StandardWrapper[default]和StandardWrapper[dispatcherServlet]):

        container.addContainerListener(this);
container.addLifecycleListener(this);
for (Container child : container.findChildren()) {
addListeners(child);
}

  虽然是递归,但是只有一棵树,所以返回的是children中的根child也就是StandardEngine[Tomcat].StandardHost[localhost],registerHost(host)这里的host就是这个根child,registerHost主要是处理映射关系包括别名和通配符并记录(mapper.addHost(host.getName(), aliases, host)),然后处理它的子容器registerContext,及子容器的子容器wrapper并将各级子容器路径关联起来:

            boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));

  注册的这些用于匹配请求的路径;然后同步代码块中启动连接同前面一样,这里什么因为解除绑定了所以都没做。

  于是TomcatEmbeddedServletContainer中的这个Tomcat对象的start就完成了,至于其中Context等子容器的Start因为不在一个线程里所以决定单独写一篇。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

Spring Boot启动过程(五):Springboot内嵌Tomcat对象的start的更多相关文章

  1. 查看和指定SpringBoot内嵌Tomcat的版本

    查看当前使用的Tomcat版本号 Maven Repository中查看 比如我们需要查Spring Boot 2.1.4-RELEASE的内嵌Tomcat版本, 可以打开链接: https://mv ...

  2. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

  3. Spring Boot启动过程(七):Connector初始化

    Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...

  4. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  5. Spring Boot启动过程(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...

  6. Spring Boot启动过程(二)

    书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...

  7. Spring Boot 启动过程

    一切从SpringApplication.run()开始,最终返回一个ConfigurableApplicationContext 构造了一个SpringApplication对象,然后调用它的run ...

  8. SpringBoot内嵌Tomcat开启APR模式(运行环境为Centos7)

    网上查到的一些springboot内嵌的tomcat开启apr的文章,好像使用的springboot版本较老,在SpringBoot 2.0.4.RELEASE中已经行不通了.自己整理了一下,供参考. ...

  9. Spring Boot启动过程(六):内嵌Tomcat中StandardHost与StandardContext的启动

    看代码有助于线上出现预料之外的事的时候,不至于心慌... StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程 ...

随机推荐

  1. SQL 2005 安装数据库镜像教程

    最近在搞在SQL 2005安装数据库镜像,中间遇到不少的错误,在此归纳总结,以方便有需要的朋友参考. 直接上脚本,主机部分: ---修改数据库为完整恢复模式USE master;ALTER DATAB ...

  2. SoapUI:入门实例

    这一章中我们要掌握如下内容: 1)         构建项目: 2)         运行单个请求: 3)         构建测试用例: 4)         接口之间传递参数,组织测试步骤: 5) ...

  3. 王爽汇编语言(第三版)环境搭建(附PDF及工具下载)

    一.前言 最近在学习汇编语言,使用的是读者评价非常高的王爽老师写的<汇编语言>(第三版),为了适应现在各个版本的windows操作系统,所以采用VMWare虚拟机来搭建纯DOS环境. 二. ...

  4. GIS制图课程目录(持续整理)

    GIS制图课程目录 by 李远祥 由于过去一年都没有进行更新,近期终于抽出时间来进行相关知识的整理,因此,对专项技术进行了不同技术线条的梳理.为了方便阅读,特意整理一下全书的目录结构,希望对读者有帮助 ...

  5. css3 3D效果

    css3 3D变形 transfrom初学 这个礼拜学了css3 3d,感觉到css无穷的魅力,可以通过几个特定的代码符号创建出3D效果的页面. ___ 透视 一个元素需要一个透视点才能激活3D空间, ...

  6. CSS3知识点整理(二)----CSS3选择器

    总结各种CSS3选择器的介绍及具体语法 (一)属性选择器 在CSS2中引入了一些属性选择器,而CSS3在CSS2的基础上对属性选择器进行了扩展,新增了3个属性选择器,使得属性选择器有了通配符的概念,这 ...

  7. Swift 2.0 字符串学习笔记(建议掌握OC字符串知识的翻阅)

    自己公司开现在使用OC语言在写,但Swift似乎是苹果更推荐使用的开发语言,估计也是未来开发的趋势,自己以前有接触swift,但又由于公司的项目赶,也没有时间去好好地学习这款开发语言.现在年底了,项目 ...

  8. Java设计和实现方法

    方法签名 方法名是驼峰命名 方法名最好能说明该方法主要做什么 方法参数的名字最好能说明该参数的意义 方法参数个数最好低于6个 例如: public void setTitleVisible(int l ...

  9. 基于Quartz.NET框架的WinForm任务计划管理工具

    最近接到一个小需求 ——可以定期同步20个Sql Server 7.0数据库里的数据(数据量会预计>10000),并保存为cvs格式文件 ——可以设置保存文件数据量 ——该应用需要用WinFor ...

  10. Java Web(五) JSP详解(四大作用域九大内置对象等)

    前面讲解了Servlet,了解了Servlet的继承结构,生命周期等,并且在其中的ServletConfig和ServletContext对象有了一些比较详细的了解,但是我们会发现在Servlet中编 ...