首先回歸一下web.xml的常用配置,看一個示例:

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationcontext-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/springmvc-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
了解web.xml的作用之前必須要了解web容器(也稱為是servlet容器)這個概念,常用的web容器有Tomcat、Jetty、JBoss等。
使用web容器,就不得不清楚什么是ServletContext。ServletContext中的信息都是由容器提供的,在web容器啟動時,它作為為公共環境容器存放公共信息,為各個Servlet提供信息,也可以作為各個Servlet之間通信的橋梁。
將應用部署到web容器中,啟動web容器后,web容器會讀取web.xml中的配置信息,對ServletContext進行信息補充。(注:配置加載順序為:context-param -> listener -> filter -> servlet)
好了,回到本文主題。
ContextLoaderListener
web.xml中的配置文件中ContextLoaderListener作為監聽器,會監聽web容器相關事件,在web容器啟動或者關閉時觸發執行響應程序。具體地,ContextLoaderListener繼承ContextLoader類並實現了ServletContextListener接口。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
1.關於監聽
ServletContextListener接口中只有初始化和銷毀的兩個方法::
public interface ServletContextListener extends EventListener { /** ** Notification that the web application initialization ** process is starting. ** All ServletContextListeners are notified of context ** initialization before any filter or servlet in the web ** application is initialized. */ public void contextInitialized ( ServletContextEvent sce ); /** ** Notification that the servlet context is about to be shut down. ** All servlets and filters have been destroy()ed before any ** ServletContextListeners are notified of context ** destruction. */ public void contextDestroyed ( ServletContextEvent sce ); }
也就是監聽ServletContext的狀態,ServletContext屬於容器對象,在用於在容器開啟或關閉時觸發事件,執行contextInitialized/contextDestroyed 。如果想了解程序執行原由可以先了解事件監聽設計模式。查閱Tomcat源碼,tomcat觸發ServletContext初始化監聽事件的源碼如下:
...(部分略) Object instances[] = getApplicationLifecycleListeners(); for (int i = 0; i < instances.length; i++) { if (!(instances[i] instanceof ServletContextListener)) continue; ServletContextListener listener = (ServletContextListener) instances[i]; try { if (noPluggabilityListeners.contains(listener)) { listener.contextInitialized(tldEvent); } else { listener.contextInitialized(event); } } catch (Throwable t) { } } ...(部分略)
也就是,遍歷所有ServletContextListener,調用監聽方法。
2.關於功能
關於功能主要看initWebApplicationContext()方法。 ContextLoader中的initWebApplicationContext()方法中會創建WebApplicationContext上下文對象,並將這個對象設置到servletContext中:
ContextLoaderListener加載過程:
2.1 創建IOC容器
initWebApplicationContext調用createWebApplicationContext方法;
-->createWebApplicationContext 調用determineContextClass方法
--> defaultStrategies中加載配置文件:
defaultStrategies.getProperty(WebApplicationContext.class.getName())
ContextLoader 類中有段靜態代碼:
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()); } currentContextPerThread = new ConcurrentHashMap(1); }
ContextLoader.properties 文件內容如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
可知,默認加載的WebApplicationContext是XmlWebApplicationContext
題外話,如果你不想使用默認的,在web.xml中配置contextClass即可
protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); ...(略)
也就是這種形式:
<context-param> <param-name>contextClass</param-name> <param-value>webApplicationContext類全路徑名稱</param-value> <context-param>
到org.springframework.web.context.support
包下查詢webApplicationContext。
2.1 IOC容器配置加載,初始化
這一步主要是調用configureAndRefreshWebApplicationContext,可知在上一步中創建了WebApplicationContext對象(ConfigurableApplicationContext類型),也就是創建了IOC容器對象,接着就要給這個對象進行配置參數的設置了。
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(); }
可知,首先會給WebApplicationContext對象設置ServletContext全局信息,然后讀取><param-name>contextConfigLocation</param-name>對應的.xml中的配置信息,例如:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationcontext-*.xml</param-value> </context-param>
這些applicationcontext-*.xml配置文件中的信息之后再執行ConfigurableApplicationContext.refresh()方法就會被加載到IOC容器中。
IOC容器的初始化過程,參考下一篇文章。