Spring的启动流程
spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。
Spring项目的标准配置就是在web.xml文件中配置前端控制器与加载spring配置文件,初始化上下文
如果不指定配置文件位置,就会去找一个默认的文件位置,这个文件就是 /WEB-INF/springDispatcherServlet-servlet.xml 文件。如果不指定配置文件,可以在web应用下的WEB-INF下创建指定文件即可。
<!--前端控制器的配置-->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置文件指定与监听器配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener:初始化上下文的监听器,继承了contenxtLoader,实现了ServletContextListener接口
在启动项目时会触发contextInitialized方法(该方法主要完成ApplicationContext对象的创建),
在关闭项目时会触发contextDestroyed方法(该方法会执行ApplicationContext清理操作)
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
// 调用ContextLoader的initWebApplicationContext方法来初始化一个启动上下文WebApplicationContext
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
this.initWebApplicationContext(event.getServletContext());:初始化应用上下文,bean的创建与建立
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化的根上下文保存在WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个属性里,如果不为空,说明已经初始化了
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 {
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 如果为空就创建一个,启动的时候肯定为空,那么就执行创建方法
if (this.context == null) {
//(1)
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);
}
//(2)
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将初始化的上下文信息放在WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性里面
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.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
} catch (Error | RuntimeException var8) {
logger.error("Context initialization failed", var8);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
throw var8;
}
}
}
(1)createWebApplicationContext:主要执行BeanUtils.instantiateClass(contextClass);方法,返回ConfigurableWebApplicationContext对象,然后执行this.context instanceof ConfigurableWebApplicationContext成立(2)
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// contextClass : "org.springframework.web.context.support.XmlWebApplicationContext"
Class<?> contextClass = this.determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
} else {
// 实际上就是创建了一个XmlWebApplicationContext对象,因为是基于配置文件进行配置的
return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
}
}
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter("contextClass");
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException var4) {
throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
}
} else {
// org.springframework.web.context.support.XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
} catch (ClassNotFoundException var5) {
throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
}
}
}
(2)最重要是wac.refresh();方法。调用AbstractApplicationContext类中的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);
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);
wac.refresh();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 1.调用容器准备刷新的方法,获取当前时间和同步标识
this.prepareRefresh();
// 2.调用子类的refreshBeanFactory()方法,Bean定义资源文件的载入从这里启动
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 3.为beanFactory配置事件处理器、类加载器等
this.prepareBeanFactory(beanFactory);
try {
// 4.为容器的某些子类指定特殊的Post事件处理器
this.postProcessBeanFactory(beanFactory);
// 5.调用所有注册的BeanFactoryPostProcesser的Bean
this.invokeBeanFactoryPostProcessors(beanFactory);
// 6.为BeanFactory注册Post事件处理器
this.registerBeanPostProcessors(beanFactory);
// 7.国际化初始化
this.initMessageSource();
// 8.初始化事件传播器
this.initApplicationEventMulticaster();
// 9.调用子类某些特殊的Bean初始化方法
this.onRefresh();
// 10.为上面的事件传播器注册事件监听器
this.registerListeners();
// 11.初始化所有单例Bean
this.finishBeanFactoryInitialization(beanFactory);
// 12.完成初始化
this.finishRefresh();
}
catch (BeansException ex) {
// 销毁已创建的Bean
this.destroyBeans();
// 取消刷新并重置标识
this.cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
this.resetCommonCaches();
}
}
}