在Tomcat服务器中启动SpringBoot项目原理(简化版)
tomcat会查询context上下文中实现ServletContainerInitializer接口的类,然后调用类的onStartup(Set<Class<?>> c, ServletContext ctx)方法
Spring的SpringServletContainerInitializer实现了这个ServletContainerInitializer接口,
会获取WebApplicationInitializer接口的实现类,调用onStartup()方法
SpringBoot的类SpringBootServletInitializer实现了Spring的WebApplicationInitializer扩展接口,
会在onStartup()方法中创建SpringApplication类,并调用SpringApplication.run()来完成启动项目
与我们在开发时调用Application.main()方法启动时一样的原理
1、在conf/server.xml文件中配置context,即项目的上下文位置(<service><Host><context>) 2、放在tomcat安装目录的webapp目录下面。
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级别的控制,如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/
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任