在【Spring源码解析】之IOC容器这一篇中我们学习了IOC容器本身的实现。下面我们看看在典型的Web容器中,Spring的IOC容器是怎么被载入和起作用的。简单的说,在Web容器中,通过ServletContext为Spring的IOC容器提供宿主环境。
下面看一下Web环境下,Web.xml文件中 spring的配置;
<!-- 配置Spring的监听器,用于初始化ApplicationContext对象 -->
<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容器中启动Spring应用程序就是一个建立上下文体系的过程。我们通过Spring的监听器配置来跟踪下代码ContextLoaderListener:
public class ContextLoaderListener implements ServletContextListener {
private ContextLoader contextLoader;
/**
* 初始化 根 web 应用的上下文
*/
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());
}
}
}
在ContextLoaderListener这个类中我们找到它初始化Initialize the root web application context.这个方法中调用的【initWebApplicationContext】方法。点进去之后我们看到它返回是这个WebApplicationContext【web应用上下文对象】。我们在WebApplicationContext这个接口中看下它的原生态的代码:
public interface WebApplicationContext extends ApplicationContext {
/**
* 这里定义的常量用于在ServletContext中存取根上下文
*/
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
* Scope identifier for request scope: "request".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_REQUEST = "request";
/**
* Scope identifier for session scope: "session".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_SESSION = "session";
/**
* Scope identifier for global session scope: "globalSession".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_GLOBAL_SESSION = "globalSession";
/**
* 对于WebApplicationContext来说需要得到Web的ServletContext
*/
ServletContext getServletContext();
}
上面的代码段是spring为Web应用程序提供的上下文接口。在一般的启动过程中,spring会使用一个默认的实现XmlWebApplicationContext【它实现了接口WebApplicationContext】。这个实现作为一个根上下文容器,在Web容器中被建立起来。具体的过程我们将分析一下。下面有请这个XmlWebApplicationContext实现出场。
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** 这儿是root上下文定义信息的默认存放位置 */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/**
* 加载bean定义信息
*/
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//这里解释一下,XMLApplicationContext,当然使用XmlBeanDefinitionReader来对bean定义信息来进行解析
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
/**
* 保留,不解释
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
/**
*使用XmlBeanDefinitionReader来读入bean定义信息
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (int i = 0; i < configLocations.length; i++) {
reader.loadBeanDefinitions(configLocations[i]);
}
}
}
/**
* 取得bean定义的位置信息,默认的路径是 /WEB-INF/applicationContext.xml
*/
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}
对于一个 Spring 激活的web 应用程序,可以通过使用Spring 代码声明式的指定在web 应用程序启动时载入应用程序上下文。