在web.xml中,配置ContextLoaderListener
<!-- 配置Listener,用來創建Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<!-- 配置Listener參數:告訴它Spring的配置文件位置,它好去創建容器 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context*.xml</param-value>
</context-param>
說明:<listener>標簽中,ContextLoaderListener用來創建Spring容器;而<context-param>標簽,用來指定了spring配置文件的位置,該兩個標簽共同起作用,也就可以在web項目啟動后,成功的創建了Spring容器了。
分析如下:
1.ContextLoaderListener實現了ServletContextListener接口,ServletContextListener是Java EE標准接口之一,類似tomcat,jetty的java容器啟動時便會觸發該接口的contextInitialized。也就是說會激活調用ContextLoaderListener的contextInitialized()方法
2.ContextLoaderListener類中的contextInitialized方法如下
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
3.我們在進入到ClassLoader類的initWebApplicationContext( )方法內部,會注入ServletContext servletContext的參數對象,同時幫我們返回一個WebApplicationContext容器對象,代碼如下:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
/** 此處省略部分代碼 */
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 {
if (this.context == null) {
/**
* 重要,該方法會幫我們創建WebApplicationConntext,並賦值給ContextLoaderListener類的context對象。
* 說明:ContextLoaderListener類的context屬性來自於它的父類
*/
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
/** 此處省略部分代碼 */
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;
}
/** 此處省略部分代碼 */
return this.context;
}
}
debug打斷點我們可以看到注入ServletContext servletContext的參數對象,是ApplicationContextFacade類型的對象,內部是通過Map來實現的,用來加載Spring的配置文件。
也就是說,該方法執行完后,我們就會拿到WebApplicationContext類型的Spring容器對象。那么該方法內部具體是如何加載Spring配置文件的呢,是如何創建WebApplicationContext對象?我們接着往下看
4.我們可以看到該行代碼:this.context = createWebApplicationContext(servletContext); 該方法用於創建WebApplicationContext對象,並賦值給context屬性。WebApplicationContext是一個接口類,本質上返回的是XmlWebApplicationContext,它就是Spring容器了
5.我們再進入到createWebApplicationContext(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);
}
會將剛才注入的ApplicationContextFacade對象傳入,用於指定Spring的配置文件位置,它的內部是通過Map來實現的,也就是說,該類會解析<context-param>標簽的內容,加載Spring配置文件,有了配置文件就可以初始化容器了
6.createWebApplicationContext( ) 調用determineContextClass( )方法
determineContextClass有如下代碼:
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
7.很明顯是從defaultStrategies中加載的
ContextLoader 類中有段靜態代碼:會加載默認配置,也就是會加載<param-value>classpath:application-context*.xml</param-value>中的xml文件
static {
try {
ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
8.ContextLoader.properties 文件內容如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
此時,determineContextClass( )方法返回的是XmlWebApplicationContext對象,之后回到3步驟中的configureAndRefreshWebApplicationContext( )方法
9. configureAndRefreshWebApplicationContext的細節調用
configureAndRefreshWebApplicationContext 調用了AbstractApplicationContext的refresh方法
refresh 方法調用了obtainFreshBeanFactory
obtainFreshBeanFactory 調用了AbstractRefreshableApplicationContext類的refreshBeanFactory方法
refreshBeanFactory調用了XmlWebApplicationContext的loadBeanDefinitions
loadBeanDefinitions中加載了對應的applicationContext.xml