Spring對於程序員說來講都不陌生;做爲一個強大的開源技術,幫助咱們可以更好的進行項目的開發與維護。 直接進入主題吧。Spring的啓動過程實際上就是Ioc容器初始化以及載入Bean的過程;本文主要是學習記錄下前半部分(Ioc容器的初始化),新手上路,若有錯誤,請指正! 1.從配置文件提及java
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
複製代碼
在通常的WEB項目中,項目的啓動通常是從web.xml文件的載入開始的。若是咱們的項目中使用了Spring,那麼你確定會在你的web.xml文件中看到上面的配置。Spring正是經過ContextLoaderListener監聽器來進行容器初始化的。下面經過代碼進行分析。程序員
2.Spring容器加載的三步走web
3.WebApplicationContext的建立過程spring
public class ContextLoaderListener extends ContextLoader implements ServletContextListener 複製代碼
從ContextLoaderListener的定義能夠看出,該監聽器繼承了ContextLoader,而且重寫了ServletContextListener中的contextInitialized和contextDestroyed方法。app
在contextInitialized中,經過調用父類(ContextLoader)的initWebApplicationContext方法進行容器建立:ide
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
複製代碼
下面來看initWebApplicationContext的代碼:學習
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//1:判斷當前容器是否存在,若是存在則報容器已經存在的異常信息
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);
//下面這個日誌就是咱們常常在啓動Spring項目時看到的日誌信息:
//Initializing Spring root WebApplicationContext
//Root WebApplicationContext: initialization started
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.
//若是當前容器爲null,則建立一個容器,並將servletContext上下文做爲參數傳遞進去,
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
//判斷當前容器是否爲可配置的,只有是Configurable的容器,才能進行後續的配置
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);
}
//三步走中的第二步:配置而且刷新當前容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//將配置而且刷新過的容器存入servlet上下文中,並以WebApplicationContext的類名做爲key值
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;
}
}
複製代碼
下面咱們在看下是如何建立WebApplicationContext的this
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//首先來肯定context是由什麼類定義的,而且判斷當前容器是否爲可配置的
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這個方法spa
protected Class<?> determineContextClass(ServletContext servletContext) {
//首先從web.xml中查看用戶是否本身定義了context
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);
}
}
/*若是沒有,則去defaultStrategies裏面取【defaultStrategies是Propertites類的/對象,在ContextLoader中的靜態代碼塊中初始化的;具體可看下下面的圖像】;默認容器是XmlWebApplicationContext*/
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);
}
}
}
複製代碼
總的來講就是:Spring的web工程首先回去檢查用戶是否本身定義了context,若是有就採用;若是沒有就使用Spring默認的。 defaultStrategies初始化: debug
至此,容器建立完成。下面是整個過程的一個流程圖(有疏漏,回頭補一個時序圖):