1.概述
通过源码我们发现,资源的定位问题主要发生在容器初始化过程中完成的,FileSystemXmlApplicationContext
、ClassPathXmlApplicationContext
在一个构造器函数中执行refresh()
容器启动的过程中完成的,当然这边启动过程中容器会有大量的复杂的初始化操作,资源的定位只是其中的一小环节。下面我们就FileSystemXmlApplicationContext
为例介绍Resource资源定位过程分析。
2.ApplicationContext集成体系介绍
这里我们需要重点关注的是
AbstractRefreshableApplicationContext的refreshBeanFactory
方法的实现,refreshBeanFactory()
方法被FileSystemXmlApplicationContext
构造函数中的refresh()
调用。在这个方法中,通过createBeanFactory()
构建了一个IoC容器供ApplicationContext
使用。这个IoC容器就是我们前面提到过的DefaultListableBeanFactory
,同时,它启动了loadBeanDefinitions()
来载入BeanDefinition
,这个过程和前面以编程的方式来使用IoC容器(XmlBeanFacoty)的过程非常类似。
3.源码分析:
我们就资源定位和加载的核心代码拿出来分析:
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
/// 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 这里是创建并设置持有的DefaultListableBeanFactory的地方
// 同时调用loadBeanDefinitions载入BeanDefinition的信息
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 资源就是在此处定位并加载的
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我们找到loadBeanDefinitions(beanFactory)
方法,此方法中完成了资源的定位和加载,主要执行如下代码:
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
我们可以看到在AbstractRefreshableApplicationContext
抽象类中它只是提供了一个抽象的模板方法,具体实现由不同的子类去覆盖实现,这边我们主要是通过FileSystemXmlApplicationContext
为例分析,所以我们找到相关的实现是在AbstractXmlApplicationContext
中去实现的:
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 为相应的BeanFactory创建XmlBeanDefinitionReader(后续xml元信息的解析就依靠此类完成)
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 环境变量设置
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 设置ResourceLoader,这边传this是因为在容器继承链中是继承了DefaultResourceLoader的
// AbstractApplicationContext 继承了DefaultResourceLoader
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);
}
AbstractXmlApplicationContext#loadBeanDefinitions(beanDefinitionReader)
方法中实现了真正的资源定位和加载:
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
* method; hence this method is just supposed to load and/or register bean definitions.
* @param reader the XmlBeanDefinitionReader to use
* @throws BeansException in case of bean registration errors
* @throws 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 {
// 获取子类覆盖实现的资源
Resource[] configResources = getConfigResources();
// 加载资源
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 获取子类中设置的资源数组的资源路径URL
String[] configLocations = getConfigLocations();
//实现加载
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
从代码中我们可以看到String[] configLocations = getConfigLocations();
资源路径的获取是在这边调用的,如果子类覆盖可此实现,那么我们直接可以从子类中获取到相应的资源文件。反过来看我们从FileSystemXmlApplicationContext
中去看,在FileSystemXmlApplicationContext
中确实是覆盖了getConfigLocations();
方法。