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