通过tomcat容器启动spring容器的启动过程
参考网站:https://www.cnblogs.com/RunForLove/p/5688731.html
通过对spring源码的解读,跟踪spring容器的启动过程,掌握SpringMVC是如何工作的;掌握Spring源码的设计和增强阅读源码的技巧。为可持续性的使用spring框架高效集成开发,解决系统一些疑难杂症提供强有力的支撑。
一、需要掌握的理论业务点:
1、Web容器初始化过程
2、Spring MVC 在web.xml中的配置
3、理解ServletContextListener
4、掌握理解ContextLoadListener
5、DispatcherServlet初始化(HttpServletBean • FrameworkServlet • DispatcherServlet)
6、ContextLoadListener与DispatcherServlet的关系
7、DispatcherServlet的设计
8、DispatcherServlet工作原理
二、分析各个理论节点的知识
1、要想了解spring容器在web容器中启动的过程,首先我们需要知道web容器在tomcat中的生命周期,方可理解spring容器是如何在web容器中启动、运行、以及销毁、的生命周期
上图展示了web容器在启动过程中主要业务走向,其官方给出的解释是:
When a web application is deployed into a container, the following steps must be performed, in this order, before the web application begins processing client requests.
1、Instantiate an instance of each event listener identified by a <listener> element in the deployment descriptor.
2、For instantiated listener instances that implement ServletContextListener, call the contextInitialized() method.
3、Instantiate an instance of each filter identified by a <filter> element in the deployment descriptor and call each filter instance's init() method.
4、Instantiate an instance of each servlet identified by a <servlet> element that includes a <load-on-startup> element in the order defined by the load-on-startup element values, and call each servlet instance's init() method.
翻译是:
当web应用程序部署到容器中时,必须按照以下顺序在web应用程序开始处理客户机请求之前执行以下步骤。
实例化由部署描述符中的元素标识的每个事件侦听器的实例。
对于实现ServletContextListener的实例化侦听器实例,调用contextInitialized()方法。
实例化由部署描述符中的元素标识的每个过滤器的实例,并调用每个过滤器实例的init()方法。
实例化由元素标识的每个servlet的实例,该元素按加载启动元素值定义的顺序包含元素,并调用每个servlet实例的init()方法。
大致的意思是,tomcat在启动web容器的时候会启动一个叫ServletContextListener的监听器,每当在web容器中有ServletContextListener这个接口被实例化的时候,web容器会通知ServletContextListener被实例的对象去执行其contextInitialized()的方法进行相应的业务处理,而spring框架在设计的过程中ContextLoadListener这个类实现了ServletContextListener这个接口,因此每当有ContextLoadListener这个类被实例化的时候,web容器会通知他执行contextInitialized()这个方法
2、spring MVC在web.xml在的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>app</display-name>
<!-- 加载Spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-starter.xml</param-value>
</context-param>
<!-- Spring监听器 -->
<listener>
<description>Spring监听器</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在web.xm配置中由<context-param>将spring的配置文件引入加载到web容器中,
<listener>配置项是在web容器中启用监听,其所配置的class是需要启用的监听器,因此spring容器的启用是同过org.springframework.web.context.ContextLoaderListener进行spring容器的启动,而ContextLoaderListener监听实现的是ServlectContextListener接口,因此在其实例化的过程中,contextInitialized()会被调用,从而进行spring容器的启动与创建的过程中
ContextLoaderListener中的contextInitialized()进行了spring容器的启动配置并且刷新实例化整个SpringApplicationContext中的Bean。因此,如果我们的Bean配置出错的话,在容器启动的时候,会抛异常出来的。
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//Spring 启动的句柄,spring容器开始启动的根目录
if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
} else {
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if(logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
//处理spring容器是否已经创建(只创建没有创建spring的各个bean)
if(this.context == null) {
this.context = this.createWebApplicationContext(servletContext);
}
if(this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
if(!cwac.isActive()) {
if(cwac.getParent() == null) {
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);
}
//Spring容器创建完成后,加载spring容器的各个组件
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
} else if(ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if(logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if(logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
} catch (RuntimeException var8) {
logger.error("Context initialization failed", var8);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
throw var8;
} catch (Error var9) {
logger.error("Context initialization failed", var9);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
throw var9;
}
}
}
以上完成Spring容器的创建 ,并在代码中配置加载Spring容器启动的配置文件,以及调用Spring容器的加载入口
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
String configLocationParam;
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
configLocationParam = sc.getInitParameter("contextId");
if(configLocationParam != null) {
wac.setId(configLocationParam);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//设置Spring容器启动的起始配置文件
configLocationParam = sc.getInitParameter("contextConfigLocation");
if(configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
}
this.customizeContext(sc, wac);
//初始化spring的各个组件,以及spring容器中的各个bean
wac.refresh();
}
Spring容器加载bean的过程入口:wac.refresh();
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
String configLocationParam;
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
configLocationParam = sc.getInitParameter("contextId");
if(configLocationParam != null) {
wac.setId(configLocationParam);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//设置Spring容器启动的起始配置文件
configLocationParam = sc.getInitParameter("contextConfigLocation");
if(configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
}
this.customizeContext(sc, wac);
//初始化spring的各个组件,以及spring容器中的各个bean
wac.refresh();
}
加spring容器中bean
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new PostProcessorRegistrationDelegate.BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList();
List<BeanPostProcessor> internalPostProcessors = new ArrayList();
List<String> orderedPostProcessorNames = new ArrayList();
List<String> nonOrderedPostProcessorNames = new ArrayList();
String[] var8 = postProcessorNames;
int var9 = postProcessorNames.length;
String ppName;
BeanPostProcessor pp;
for(int var10 = 0; var10 < var9; ++var10) {
ppName = var8[var10];
if(beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if(pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
} else if(beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
} else {
nonOrderedPostProcessorNames.add(ppName);
}
}
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, (List)priorityOrderedPostProcessors);
List<BeanPostProcessor> orderedPostProcessors = new ArrayList();
Iterator var14 = orderedPostProcessorNames.iterator();
while(var14.hasNext()) {
String ppName = (String)var14.next();
BeanPostProcessor pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if(pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, (List)orderedPostProcessors);
//存放spring容器中加载的各个bean
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList();
Iterator var17 = nonOrderedPostProcessorNames.iterator();
while(var17.hasNext()) {
ppName = (String)var17.next();
pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if(pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, (List)nonOrderedPostProcessors);
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, (List)internalPostProcessors);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
通过SerlvetContextListener这一特性还可以做其它的运用,如缓存数据在服务启动过程中的初始化
https://blog.csdn.net/qq_31854907/article/details/86304955
THE_FU: 调用步骤 controller ——》 serviceImpl方法1 —— 》 私有方法1(需要校验入参的方法)
THE_FU: 你好 我有个问题想问一下,在serviceimpl里有个私有方法想要校验参数 使用Validated 和 Valid都不生效 这是为什么
weixin_peng: 毫无意义的博客
a515370: redis.call('setnx', KEYS[1], ARGV[2]) 有问题, key没失效前不能解锁
中静~: 一个类实现了 spring的接口 并不会 交给spring管理 class B implements FactoryBean, InitializingBean 还需要 import 这个B,或者加上配置注解