SpringBoot学习系列-war包在tomcat下启动原理

springBoot项目 war包启动原理
参考链接:

【spring boot war包启动原理】 https://www.cnblogs.com/stone-with-big-ears/p/10950581.html
【Tomcat war包加载过程】https://www.jianshu.com/p/6e435a5a4fee

spring boot war启动是利用Servlet 3.0新增的ServletContainerInitializer接口结合SPI(Service Provider Interface)机制实现的。

springBoot的application.java是应用程序启动入口,它的父类,org.springframework.boot.web.servlet.support.SpringBootServletInitializer

public abstract class SpringBootServletInitializer implements WebApplicationInitializer

springBoot使用war的启动方式原理实现:

通过spring-web-xxx.jar包的org.springframework.web.SpringServletContainerInitializer类实现ServletContainerInitializer接口,从而达到无web.xml配置的启动

主要逻辑

Spring在spring-web-version.jar/META-INF/services/javax.servlet.ServletContainerInitializer文件中,
配置了spring对ServletContainerInitializer接口的实现类org.springframework.web.SpringServletContainerInitializer。

Servlet Container启动阶段扫描jar包中META-INF/services/javax.servlet.ServletContainerInitializer文件,
获取ServletContainerInitializer实现类并实例化,解析ServletContainerInitializer上@HandlesTypes注解,
查找出@HandlesTypes限定的类型集合,作为ServletContainerInitializer.onStartup方法处理的第一个参数c。

Servlet Container依次调用每个ServletContainerInitializer实例的onStartup。war包启动的场景中会调用SpringServletContainerInitializer.onStartup方法,
该方法循环调用c集合中每个 WebApplicationInitializer子类(即SpringBootServletInitializer)的onStartup方法。

SpringBootServletInitializer.onStartup方法调用SpringBootServletInitializer.createRootApplicationContext方法,
createRootApplicationContext方法中构建SpringApplication并执行SpringApplication.run方法以启动整个spring项目。

tomcat启动主要的相关类,启动顺序从上到下

BootStrap
  Catalina
    StandardServer
        StandardService
            // 从这里开始就是属于Container 容器范畴
            StandardEngine
                StandardHost
                    StandardContext
                        StandardWrapper

StandardEngine 表示servlet引擎容器
StandardHost 表示虚拟host容器
StandardContext 表示servlet上下文,一个独立的web应用程序

从StandardContext开始
因为engine,host,context,StandardWrapper都继承自ContainerBase类,
ContainerBase extends LifecycleMBeanBase
LifecycleMBeanBase extends LifecycleBase
所以,这些container类都实现了Lifecycle接口的监听器

在StandardContext中,它的监听器类是:ContextConfig
解析应用程序的操作都是在监听器中完成了
ContextConfig.java

    ContextConfig.java
    public void lifecycleEvent(LifecycleEvent event) {
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            configureStart();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        ...
    }
ContextConfig.configureStart(){
。。。
 webConfig()
。。。
}

在webConfig()方法
  对应用程序扫描web.xml文件
  处理为其他所有内容之后添加的web片段,因此其他所有内容均具有优先权
  将Servlet标记为可覆盖,SCI配置可以替换默认配置

webConfig(){
    。。。
    // Parse context level web.xml
    InputSource contextWebXml = getContextWebXmlSource();
    if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
        ok = false;
    }
    。。。
    

}

web配置步骤

         servlet context级别的web.xml解析
            如果没有找到应用程序的/WEB-INF/web.xml文件,默认会设置标志位ok=true,用来后续的ServletContainerInitializer实现类处理
            
        1.扫描应用程序/WEB-INF/lib路径下的JAR,未找到/META-INF/web-fragment.xml文件的jar包添加到生成的Map中。
            如果任何应用程序JAR都具有 web-fragment.xml文件,则将在此时进行解析。 web-fragment.xml容器提供的JAR文件将被忽略。
            Servlet3.0 WebFragment扫描
        2.确定web-fragments这些片段的启动顺序
        3.查找所有ServletContainerInitializer实现类,
            处理ServletContainerInitializers的实现类,这也是servlet 3.0新增的特性,
            容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,
            并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup() 方法处理,
            我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类,
            过滤掉不希望给 onStartup() 处理的类。在onStartup方法中可以优先加载这些类,
            或者修改其中的方法等。
            这步主要是把这些类找到放到Set<ServletContainerInitializer> scis中;
        4.将应用中的web.xml与orderedFragments进行合并,合并在WebXml类的merge方法中实现
        5.将应用中的web.xml与全局的web.xml文件(conf/web.xml和web.xml.default)进行合并
        6.用合并好的WebXml来配置Context,这一步在处理servlet时,会为每个servlet创建一个wrapper,
            并调用addChild将每个wrapper作为context子容器,后续分析


原文地址:https://www.cnblogs.com/gne-hwz/p/13088185.html