spring容器的啟動過程是什么?
spring在web容器中,啟動過程是Servlet 容器對spring環境的構造,初始化,裝配的過程。
spring的啟動過程
1.通過ContextLoaderListener監聽作為啟動spring的入口
啟動必要條件:在web.xml中配置
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
ContextLoaderListener(spring中的類)繼承ContextLoader(spring中的類),并實現ServletContextListener(servlet中的接口),ServletContextListener監聽ServletContext,當容器啟動時,會觸發ServletContextEvent事件,該事件由ServletContextListener來處理,啟動初始化ServletContext時,調用contextInitialized方法。而ContextLoaderListener實現了ServletContextListener,所以,當容器啟動時,觸發ServletContextEvent事件,讓ContextLoaderListener執行實現方法contextInitialized(ServletContextEvent sce);
這部分源碼為:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
}
2.通過initWebApplicationContext方法來初始化WebApplicationContext
WebApplicationContext是spring中的上下文。它的作用等同于Servlet中的ServletContext。
(部分注釋源碼被我刪掉)
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
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!");
}
try {
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
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 ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
initWebApplicationContext(ServletContext servletContext)方法是ContextLoader中的方法。它的作用是制作一個WebApplicationContext上下文,并將這個上下文保存在servletContext中,并保存在當前ContextLoader實例中。
3.如何初始化WebApplicationContext
上面源碼中的
this.context = createWebApplicationContext(servletContext);
用來制造一個WebApplicationContext,制造的過程,依賴ServletContext。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
通過determineContextClass(ServletContext servletContext)方法獲取需要實例化的context類的class,通過BeanUtils.instantiateClass(contextClass)將這個class用反射的手段實例化WebApplicationContext 。
那么determineContextClass怎樣來確定實例化那個context類那?(spring有很多的context類實現了WebApplicationContext ,當然這個context類也可以是我們自己寫的,具體實例化那個類,在web.xml中配置)
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
從上面的代碼可以看出,先從servletContext中找我們在web.xml中有沒有配置要實例化那個上下文context,如果配置了
<context-param>
<param-name>contextClass</param-name>
<param-value>rg.springframework.web.context.support.StaticWebApplicationContext</param-value>
</context-param>
那么將實例化StaticWebApplicationContext這個上下文。注意:這個地方的param-name必須是contextClass(約定成俗的,其實就是是程序寫死的)。如果沒有這個配置,那么程序將找到一個叫ContextLoader.properties的配置文件,這個配置文件注明了一個默認的上下文:XmlWebApplicationContext。這個XmlWebApplicationContext實例化的過程是制造一個ResourcePatternResolver的實例,這個實例將會在后面的spring啟動過程中起到關鍵作用。
最后流程圖: