Spring 和 Struts在web.xml中增加的配置:
1: <!-- spring的配置 -->
2: <context-param>
3: <param-name>contextConfigLocation</param-name>
4: <param-value>classpath:/SpringContext/applicationContext-web.xml</param-value>
5: </context-param>
7:
8: <listener>
9: <listener-class>org.springframework.web.context.ContextLoaderListener
10: </listener-class>
11: </listener>
12:
13: <filter>
14: <filter-name>struts2</filter-name>
15: <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
17: <init-param>
18: <param-name>config</param-name>
19: <param-value>struts-default.xml,struts-plugin.xml,struts/struts.xml</param-value>
20: </init-param>
21: </filter>
22:
23: <filter-mapping>
24: <filter-name>struts2</filter-name>
25: <url-pattern>*.do</url-pattern>
26: </filter-mapping>
第一個(gè)tag定義的是spring的配置文件地址到環(huán)境參數(shù)(context parameter)
第二個(gè)tag定義一個(gè)listener為org.springframework.web.context.ContextLoaderListener,這里相當(dāng)于j2ee容器給我們提供的main函數(shù)的切入點(diǎn),可以讓我們做一些系統(tǒng)初始化的工作,需要實(shí)現(xiàn)的類是:javax.servlet.ServletContextListener
第三個(gè)tag則定義了struts2的一個(gè)filter。Filter則是對(duì)每次請(qǐng)求(可以通過(guò)filter-mapping指定)做過(guò)濾處理,請(qǐng)求首先請(qǐng)過(guò)filter鏈的處理,然后再到HttpServlet的init方法。對(duì)應(yīng)的類是:javax.servlet.Filter。上面先配置了一個(gè)filter,對(duì)應(yīng)的類是org.apache.struts2.dispatcher.FilterDispatcher,參數(shù)則是struts的配置文件位置
第四個(gè)參數(shù)定義了filter怎樣行為,顯然它對(duì).do為后綴的請(qǐng)求應(yīng)用struts2這個(gè)名稱的filter
查看ContextLoaderListener可知,它正好繼承了javax.servlet.ServletContextListener,用于監(jiān)聽(tīng)javax.servlet.ServletContextEvent事件
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.context;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* Bootstrap listener to start up Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader}.
*
* <p>This listener should be registered after
* {@link org.springframework.web.util.Log4jConfigListener}
* in <code>web.xml</code>, if the latter is used.
*
* @author Juergen Hoeller
* @since 17.02.2003
* @see ContextLoaderServlet
* @see org.springframework.web.util.Log4jConfigListener
*/
public class ContextLoaderListener implements ServletContextListener {
private ContextLoader contextLoader;
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
/**
* Create the ContextLoader to use. Can be overridden in subclasses.
* @return the new ContextLoader
*/
protected ContextLoader createContextLoader() {
return new ContextLoader();
}
/**
* Return the ContextLoader used by this listener.
* @return the current ContextLoader
*/
public ContextLoader getContextLoader() {
return this.contextLoader;
}
/**
* Close the root web application context.
*/
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
}
}
此類implement了ServletContextListener的兩個(gè)方法:
public void contextInitialized(ServletContextEvent event);
public void contextDestroyed(ServletContextEvent event);
分別做context的初始化和銷毀
另外提供了一個(gè)protected方法:protected ContextLoader createContextLoader() 用于創(chuàng)建真正做事情的代理類CotextLoader
和一個(gè)public方法:public ContextLoader getContextLoader();
可見(jiàn),這個(gè)Listener類直接將工作代理給了ContextLoader類了
____________________________________________________________________________________________________________________________
我們按圖索驥,下面再分析org.springframework.web.context.ContextLoader
ContextLoader里面有個(gè)私有成員:private WebApplicationContext context
此變量提供了create方法:
createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)
因?yàn)镾pring提供了多種WebApplicationContext類,所以需要一個(gè)方法來(lái)決定使用哪個(gè)WebApplicationContextContext類
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName);
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
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);
}
}
}
public static final String CONTEXT_CLASS_PARAM = "contextClass";
所以如果在web.xml中的<context-param> </context-param>中定義了參數(shù)contextClass,那么直接就決定了用此Context類
否則,就應(yīng)用缺省策略來(lái)決定使用哪個(gè)Context類。
缺省策略如下:
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
實(shí)際上,缺省策略從org\springframework\web\context\ContextLoader.properties 文件中取得屬性org.springframework.web.context.WebApplicationContext
我們看看ContextLoader.properties文件的內(nèi)容:
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
顯然,缺省的Context類就是 XmlWebApplicationContext類
!!!真累啊,Spring...
OK,總算知道用哪個(gè)Context類了,那么現(xiàn)在到了create這個(gè)Context實(shí)例的時(shí)候了
protected WebApplicationContext createWebApplicationContext(
ServletContext servletContext, ApplicationContext parent) throws BeansException {
Class contextClass = determineContextClass(servletContext);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setParent(parent);
wac.setServletContext(servletContext);
wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(servletContext, wac);
wac.refresh();
return wac;
}
這里很容易理解,只不過(guò)它的創(chuàng)建不是直接new,而是封裝了一層,調(diào)用BeanUtils.instantiateClass()工具方法
接下來(lái)設(shè)定WebApplicationCcontext實(shí)例的parent, servletContext,
其中配置文件位置(web.xml中)的contextConfigLocation參數(shù)指定的
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/SpringContext/applicationContext-web.xml
</param-value>
</context-param>
倒數(shù)第二行調(diào)用的customizeContext()方法目前Spring實(shí)現(xiàn)代碼是空的,估計(jì)留作以后(看來(lái)萬(wàn)事要留余地啊^_^)
最后一件事就是調(diào)用WebApplicationContext的refresh()方法。(這個(gè)方法是個(gè)stratup方法,很重要。他干的事情后面會(huì)著重涉及)
最后就是真正做事的initWebApplicationContext()方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
throws IllegalStateException, BeansException {
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!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), 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;
}
}
它其實(shí)做得事情很簡(jiǎn)單,調(diào)用loadParentContext()方法獲取父context,調(diào)用createWebApplicationContext()創(chuàng)建WebApplicationContext實(shí)例
一切都似乎完成了,至少我們明白了配置文件是如何加載進(jìn)去的,至于IoC容器如何幫你注入配置文件中的bean,下次再探索。線索是什么呢?你還記得在createWebApplication()的最后一步做什么了嗎?refresh(), 對(duì)了,這個(gè)就是你作為職業(yè)"嘿客"要探索的下一個(gè)線索