在web工程中,通过在web.xml中配置ContextLoaderListener来启动配置spring容器,常用配置实例如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/applicationContext.xml
classpath*:/applicationContext-haze.xml
</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>production</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
其中contextConfigLocation指定了spring配置文件位置,spring.profiles.default指定了加载哪种profile, 一般常用profile有以下三种,production,develop和test, profile可以通过在配置文件中指定<bean profile="">元素来指定(profile命名可以随便起).
ContextLoaderListener类定义如下:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
从类定义可以看出,ContextLoaderListener继承ContextLoader并实现了ServletContextListener,所以在容器启动时候会调用如下代码:
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
initWebApplicationContext方法为ContextLoader中定义的方法,该方法即完成Spring ApplicationContext配置启动入口.
进入ContextLoader源码中可以看到在ContextLoader中有一个static静态代码块,代码如下:
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());
}
}
以上代码加载默认
ContextLoader.properties,
打开该文件可以看到该文件中内容如下:
# 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
因在web环境中,Spring使用WebApplicationContext作为Spring上下文,故该文件指定了使用XmlWebApplicationContext这个类作为WebApplicationContext接口的实现,当然你也可以通过在web.xml中通过context-param指定自定义实现类,如下代码所示:
<context-param>
<param-name>contextClass</param-name>
<param-value>
自定义WebApplicationContext实现类
</param-value>
</context-param>
下面看initWebApplicationContext(ServletContext servletContext)这个方法:
该方法主要用来完成Spring WebApplicationContext上下文配置启动工作,代码流程如下:
1.首先判断在servletContext中是否包含key=WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的上下文信息,如果不存在则通过
createWebApplicationContext(servletContext)方法创建WebApplicationContext类的实例对象并保存在ContextLoader中的this.context变量中.2.createWebApplicationContext(servletContext)方法中通过contextClass获取并实例化WebApplicationContext对象,默认即实例化ContextLoader.properties 中指定的org.springframework.web.context.support.XmlWebApplicationContext对象. 3.判断this.context是否属于ConfigurableWebApplicationContext对象,如果是则执行方法,因XmlWebApplicationContext实现了ConfigurableWebApplicationContext接口,所以该方法会执行,该方法代码如下:configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)
其中,设置WebApplicationContext的ID和configLocation以及初始化Enviroment对象,在该方法的最下一行通过调用wac.refresh()来完成实际上下文初始化工作.protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } customizeContext(sc, wac); wac.refresh(); }
reshresh()方法是在ConfigurableApplicationContext接口中定义的,具体实现是在AbstractApplicationContext类中实现.
XmlWebApplicationContext中主要完成了如下功能:
- 默认Spring上下文配置文件路径"/WEB-INF/applicationContext.xml",当找不到自定义的configLocation时会去默认路径读取.
实现方法是在AbstractRefreshableConfigApplicationContext中的getConfigLocations()中,代码如下:在XmlWebApplicationContext中重写了getDefaultConfigLocations(),代码如下:protected String[] getConfigLocations() { return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); }
@Override 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}; } }
- 重写loadBeanDefinitions()方法,该方法在AbstractApplicationContext中的refresh()过程中调用,具体完成beanDefinition的读取加载工作.
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(getEnvironment()); 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); } /** * Initialize the bean definition reader used for loading the bean * definitions of this context. Default implementation is empty. * <p>Can be overridden in subclasses, e.g. for turning off XML validation * or using a different XmlBeanDefinitionParser implementation. * @param beanDefinitionReader the bean definition reader used by this context * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass */ protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } /** * Load the bean definitions with the given XmlBeanDefinitionReader. * <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method; * therefore this method is just supposed to load and/or register bean definitions. * <p>Delegates to a ResourcePatternResolver for resolving location patterns * into Resource instances. * @throws IOException if the required XML document isn't found * @see #refreshBeanFactory * @see #getConfigLocations * @see #getResources * @see #getResourcePatternResolver */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }
在loadBeanDefinitions方法中首先实例化一个XmlBeanDefinitionReader对象,然后通过XmlBeanDefinitionReader对象的loadBeanDefinitions(String configLocation) 方法来完成加载工作.