Spring MVC 解析之 DispatcherServlet之ContextLoaderListener容器初始化
阿新 • • 發佈:2018-12-14
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在tomcat啟動時,spring通過上述配置會初始化spring容器,注入applicationContext.xml中配置的bean以及其他一些配置。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
ContextLoderListener實現ServletContextListener介面,而該介面繼承java的監聽標記介面EventListener,表示當servlet容器啟動時,就會接受通知,進而進行一系列的初始化操作。
public interface ServletContextListener extends EventListener { /** * Receives notification that the web application initialization * process is starting. * * <p>All ServletContextListeners are notified of context * initialization before any filters or servlets in the web * application are initialized. * * @param sce the ServletContextEvent containing the ServletContext * that is being initialized */ public void contextInitialized(ServletContextEvent sce); /** * Receives notification that the ServletContext is about to be * shut down. * * <p>All servlets and filters will have been destroyed before any * ServletContextListeners are notified of context * destruction. * * @param sce the ServletContextEvent containing the ServletContext * that is being destroyed */ public void contextDestroyed(ServletContextEvent sce); }
spring的具體初始化工作均在父類ContextLoder中進行,具體初始化步驟如下:
判斷是否已經存在WebApplicationContext(spring容器),如果存在,則丟擲異常。 建立WebApplicationContext容器,從web.xml中獲取context-param名稱為contextClass的value,該類必須實現WebApplicationContext介面,如果沒有指定,則預設使用xmlWebApplicationContext。 判斷建立的WebApplicationContext容器是否為ConfigurableApplicationContext的例項或子類,如果是,則判斷該容器是否至少重新整理一次並且還沒有關閉(是否有效),如果無效,則重新設定容器的id,並載入對應的contextConfigLocation配置(spring配置), 載入父容器,spring提供一個模板方法,如果需要配置父容器,可以繼承ContextLoder,並覆蓋loadParentContext方法。 執行緒繫結容器
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
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!");
}
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 {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
//建立容器
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//如果容器無效,則重置contextId,且重新載入contextConfigLocation配置
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
//執行緒繫結容器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;
}
}