zoukankan      html  css  js  c++  java
  • 在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
    作者:海绵般汲取
    出处:https://www.cnblogs.com/gne-hwz/
    版权:本文版权归作者和博客园共有
    转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
  • 相关阅读:
    web设计经验<一> 提升移动设备响应式设计的8个建议
    web设计经验<九>教你测试手机网页的5大方法
    HTML5吧!少年
    用java页面下载图片
    在springmvc中,获取Connection接口
    360记住用户信息
    360浏览器Uncaught TypeError: object is not a function问题
    validation插件
    上传附件验证方法
    瀑布流布局
  • 原文地址:https://www.cnblogs.com/gne-hwz/p/13959919.html
Copyright © 2011-2022 走看看