首先回歸一下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容器的初始化過程,參考下一篇文章。
