原文:http://blog.csdn.net/fuliangliang/archive/2006/12/22/1454382.aspx
首先从WEB.XML入手
==>web.xml
代码
<param-name>webAppRootKey</param-name>
<param-value>task.root</param-value>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/taskContext*.xml</param-value>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
进入contextLoaderListener看看到底加载时做了甚么
==>org.springframework.web.context.ContextLoaderListener
代码
public class ContextLoaderListener implements ServletContextListener {
private ContextLoader contextLoader;
/**
Initialize the root web application context.
*/
//当WEB上下文初始化时,系统会调用此方法
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
//监听到WEB上下文初始化的时候执行SPRING上下文contextLoader的初始化工作
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.
*/
public ContextLoader getContextLoader() {
return contextLoader;
}
/**
Close the root web application context.
*/
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
}
}
看一下是怎么来初始化webapplicationContext的
==>contextLoader.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!");
}
long startTime = System.currentTimeMillis();
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
servletContext.log("Loading Spring root WebApplicationContext");
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.
//创建web上下文
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
if (logger.isInfoEnabled()) {
logger.info("Using context class [" + this.context.getClass().getName() +
"] for root WebApplicationContext");
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext [" + this.context +
"] 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;
}
}
==>contextLoader.createWebApplicationContext(servletContext, parent);
代码
protected WebApplicationContext createWebApplicationContext(
ServletContext servletContext, ApplicationContext parent) throws BeansException {
//根据servletContext来决定要实例化的WebApplicationContext
Class contextClass = determineContextClass(servletContext);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setParent(parent);
wac.setServletContext(servletContext);
//得到WEB.XML中设置的SPRING配置文件位置
String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocation != null) {
//把配置文件分段后设置到WebApplicationContext的ConfigLocations中
wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
//刷新WebApplicationContext
wac.refresh();
return wac;
}
==>contextLoader.determineContextClass(servletContext);
代码
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
//获得需要实例化的CONTEXT类名,在web.xml中有设置,如果没有设置,那么为空
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);
}
}
//如果在spring web.xml中没有设置context类位置,那么取得默认context
else {
//取得defaultStrategies配置文件中的WebApplicationContext属性
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName);
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
SPRING上下文默认的策略是甚么呢?
==>contextLoader.defaultStrategies
代码
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 {
//设置classpath为contextLoader同级目录
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
//加载该目录下的所有properties文件
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
找到同级目录下的配置文件
==>ContextLoader.properties
代码
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
==>org.springframework.web.context.support.XmlWebApplicationContext
代码
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/* Default config location for the root context /
//配置了默认的spring配置文件
public static final String DEFAULT_CONFIG_LOCATION = “/WEB-INF/applicationContext.xml”;
//配置文件默认BUILD路径
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = “/WEB-INF/“;
//配置文件默认后缀名
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = “.xml”;
/**
Loads the bean definitions via an XmlBeanDefinitionReader.
@see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
@see #initBeanDefinitionReader
@see #loadBeanDefinitions
*/
//获得bean配置
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
//从BEAN工厂获得一个XmlBeanDefinitionReader 来读取SPRING配置文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//设置beanDefinitionReader服务于当前CONTEXT
// 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);
}
/**
Initialize the bean definition reader used for loading the bean
definitions of this context. Default implementation is empty.
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.
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.
Delegates to a ResourcePatternResolver for resolving location patterns
into Resource instances.
@throws org.springframework.beans.BeansException in case of bean registration errors
@throws java.io.IOException if the required XML document isn't found
@see #refreshBeanFactory
@see #getConfigLocations
@see #getResources
@see #getResourcePatternResolver
*/
//读取配置文件
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]);
}
}
}
/**
The default location for the root context is “/WEB-INF/applicationContext.xml”,
and “/WEB-INF/test-servlet.xml” for a context with the namespace “test-servlet”
(like for a DispatcherServlet instance with the servlet-name “test”).
*/
//获得默认的ConfigLocations
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};
}
}
==>AbstractBeanDefinitionReader.loadBeanDefinitions()
代码
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//根据配置文件读取相应配置
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
这个是其中一个ResourceLoader的实现
==>PathMatchingResourcePatternResolver.getResources(String locationPattern);
代码
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
==>PathMatchingResourcePatternResolver.findPathMatchingResources(String locationPattern);
代码
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
//collectionFactory初始化一个set容量为16
Set result = CollectionFactory.createLinkedSetIfPossible(16);
for (int i = 0; i < rootDirResources.length; i++) {
Resource rootDirResource = rootDirResources[i];
if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return (Resource[]) result.toArray(new Resource[result.size()]);
}
前面说到有一个刷新WebApplicationContext的操作,但是XmlWebApplicationContext 并没有实现refresh方法,而方法的实现写在
AbstractRefreshableWebApplicationContext 中
==>AbstractRefreshableWebApplicationContext.refresh();
代码
public void refresh() throws BeansException {
if (ObjectUtils.isEmpty(getConfigLocations())) {
//设置configLocations为默认的getDefaultConfigLocations()
setConfigLocations(getDefaultConfigLocations());
}
super.refresh();
}
==>AbstractApplicationContext.refresh();
代码
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
this.startupTime = System.currentTimeMillis();
synchronized (this.activeMonitor) {
this.active = true;
}
// Tell subclass to refresh the internal bean factory.
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// Tell the internal bean factory to use the context's class loader.
beanFactory.setBeanClassLoader(getClassLoader());
// Populate the bean factory with context-specific resource editors.
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
// Configure the bean factory with context semantics.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered with the context instance.
for (Iterator it = getBeanFactoryPostProcessors().iterator(); it.hasNext();) {
BeanFactoryPostProcessor factoryProcessor = (BeanFactoryPostProcessor) it.next();
factoryProcessor.postProcessBeanFactory(beanFactory);
}
if (logger.isInfoEnabled()) {
if (getBeanDefinitionCount() == 0) {
logger.info("No beans defined in application context [" + getDisplayName() + "]");
}
else {
logger.info(getBeanDefinitionCount() + " beans defined in application context [" +
getDisplayName() + “]“);
}
}
try {
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors();
// Register bean processors that intercept bean creation.
registerBeanPostProcessors();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate singletons this late to allow them to access the message source.
beanFactory.preInstantiateSingletons();
// Last step: publish corresponding event.
publishEvent(new ContextRefreshedEvent(this));
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
beanFactory.destroySingletons();
throw ex;
}
}
}