在Tomcat服务器中启动SpringBoot项目原理(简化版)

总的来说,tomcat方式启动WAR包项目,
tomcat会查询context上下文中实现ServletContainerInitializer接口的类,然后调用类的onStartup(Set<Class<?>> c, ServletContext ctx)方法
Spring的SpringServletContainerInitializer实现了这个ServletContainerInitializer接口,
会获取WebApplicationInitializer接口的实现类,调用onStartup()方法

SpringBoot的类SpringBootServletInitializer实现了Spring的WebApplicationInitializer扩展接口,
会在onStartup()方法中创建SpringApplication类,并调用SpringApplication.run()来完成启动项目
与我们在开发时调用Application.main()方法启动时一样的原理

 

首先java web服务器,如tomcat,存在着配置要让服务器加载web项目方式
1、在conf/server.xml文件中配置context,即项目的上下文位置(<service><Host><context>2、放在tomcat安装目录的webapp目录下面。
tomcat服务器默认会加载context对象路径下的WEB-INF/web.xml文件,进行context的初始化

tomcat启动到加载项目及项目初始化完成的流程

Tomcat启动时类从上到下关系

StandardServer
    StandardService
        StandardEngine
server,service,engine这三个是固定加载了,实现了抽象类LifeCycleBase 用于事件触发

子容器container包含关系 继承了抽象类ContainerBase类,其也继承了LifeCycleBase StandardEngine StandardHost StandardContext StandardWrapper

上面的类都实现了lifeCycle事件监听接口,用于触发这些类从init(),start(),stop(),destroy()这些生命周期过程中的触发的事件,给事件监听器做相应处理。

而这个接口的实现类org.apache.catalina.util.LifecycleBase又将这些事件进行了细分,重写了上面的4个接口方法,变成8个触发事件

init()初始化事件==》 (初始化前:LifecycleState.INITIALIZING)和(初始化后:LifecycleState.INITIALIZED)

  重写init方法后变成

            setStateInternal(LifecycleState.INITIALIZING, null, false);
            initInternal(); // 真正的类初始化
            setStateInternal(LifecycleState.INITIALIZED, null, false);

start() 启动事件==》 启动前 和 启动后

  真正的启动 startInternal()

。。。

 

上面说到了ContainerBase抽象类,其在上面8个事件的基础上,

额外实现了Container容器的事件监听addChild(添加子容器),addValve(添加容器容器关联的Pipeline对象),removeChild,removeValve。

tomcat启动到加载context过程

1、启动
2、Catalina.load()方法
    在这里面parseServerXml(true);方法会动态解析conf/server.xml标签,
    然后将解析生成的StandardServer,Listener事件监听,StandardService,StandardThreadExecutor
    其他的Connector,Engine,Host,Context等节点为其配置的解析规则对象,用于xml配置这些节点时,
    也会将动态创建的对象设置到对应的父节点上。
    具体的解析规则设置查看Catalina.createStartDigester()方法
3、 所有必要类的初始化
    StandardServer.initInternal()
        加载服务器全局jar,调用service.init(),触发事件。。。
    StandardService.initInternal()
        调用engine.init(),executor.init(),connector.init()
    StandardEngine.initInternal()
    
    StandardServer.startInternal()
    StandardService.startInternal()
    StandardEngine.startInternal()
        children.startInternal(),即调用host.start()
    StandardHost.startInternal()
        这里触发的事件监听器里有操作:监听器:(HostConfig implements LifecycleListener)
        会查询<Host>节点的appBase="webapps"扫描该路径下的文件夹:会扫描war包 和 已解压的文件 (扫描项目的WEB-INF/web.xml文件解析生成一个context对象)
        contex描述文件部署:将Server中的context的配置单独拿出生成一个xml文件,Host扫描这些文件的路径由Host的xmlBase属性指定
        从这里就开始的项目的加载

后面的步骤就是

StandardContext.startInternal() 

  触发listener事件监听器,

StandardWrapper.startInternal()

  standardWrapper:这是一个Servlet

 

我们从上面大体流程可以知道StandardContext的创建是通过Host.start()容器的事件监听器HostConfig.deployApps(name)来操作,

   1、部署war包,
2、部署已解压的的文件夹,
3、部署XML descriptor,conf
/server.xml文件的<Host>节点下的<Context>节点对应的项目 然后使用xml解析器解析生成StandardContext容器(解析xml中配置的context时,context = digest.parse(File) 会自动创建org.apache.catalina.core.StandardContext对象), 然后调用将context添加到host容器中:host.addChild(context); 后面就是调用StandardContext.initInternal() , StandardContext.startInternal()

StandardContext.initInternal() 没有做什么操作,将bena注册到JmxMBeanServer 管理服务中,广播bean创建通知

standarContext.startInternal( ), 

 

 

j2eeType=WebModule,name=//localhost/Spring_redis_war_exploded,J2EEApplication=none,J2EEServer=none

/org/apache/catalina/util/CharsetMapperDefault.properties


增加事件监听器:LifecycleListener
加载webapp资源
    WebAppLoader 创建WebappClassLoaderBase
    加载/WEB-INF/classes
    加载 /WEB-INF/lib
发送configure_start_event事件,然后会通知ContextConfig调用configureStart()方法
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
    webConfig(); 扫描web.xml,与全局的规则配置进行合并,
        解析web.xml中配置的listener,servlet,filter创建添加到StandardContext中。
        xml解析规则配置类WebRuleSet.addRuleInstances,
        解析类WebXmlParser
            先后调用addContextParam,addFilter,addFilterMapping,
            addListener,addServlet,setMaxFileSize,setMaxRequestSize,addServletMapping
        
        2、processServletContainerInitializers()查询ServletContainerInitializer实现类
    applicationAnnotationsConfig

 org.apache.catalina.startup.ContextConfig.configureStart() 方法中会触发webConfig()方法

会在这个方法里调用processServletContainerInitializers()方法

这个方法会查找META-INF/services/路径下配置好的实现了ServletContainerInitializer接口的类,

如果ServletContainerInitializer接口实现类有注解@HandlesTypes(xxx.class),也会添加到StandardContext的initializers中

private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
new LinkedHashMap<>();

会在StandardContext.startInternal()方法中会遍历调用initializers的onStarup()方法

// Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

 

 

 

Spring是基于Servlet容器的java框架

也就是说Spring对请求响应的控制是基于Servlet级别的控制,如JFinal框架是基于Filter级别的控制

spring在web.xml中主要的配置

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/spring-context*.xml</param-value>
    </context-param>

    <!--Spring MVC Servlet controller控制器配置-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/spring-mvc*.xml</param-value>
        </init-param>
        <!-- 当load-on-startup的值为1时,表示启动容器时,初始化Servlet  -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <!--servlet容器映射到所有路径-->
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

设置Servlet容器

org.springframework.web.servlet.DispatcherServlet

设置context加载过程中需要用到的ServletContextListener,进行系统资源的初始化

org.springframework.web.context.ContextLoaderListener

 

posted @ 2021-03-01 18:07  海绵般汲取  阅读(1193)  评论(0编辑  收藏  举报