1.概述
通過源碼我們發(fā)現(xiàn),資源的定位問題主要發(fā)生在容器初始化過程中完成的,FileSystemXmlApplicationContext
、ClassPathXmlApplicationContext
在一個構(gòu)造器函數(shù)中執(zhí)行refresh()
容器啟動的過程中完成的,當(dāng)然這邊啟動過程中容器會有大量的復(fù)雜的初始化操作,資源的定位只是其中的一小環(huán)節(jié)。下面我們就FileSystemXmlApplicationContext
為例介紹Resource資源定位過程分析。
2.ApplicationContext集成體系介紹
這里我們需要重點關(guān)注的是
AbstractRefreshableApplicationContext的refreshBeanFactory
方法的實現(xiàn),refreshBeanFactory()
方法被FileSystemXmlApplicationContext
構(gòu)造函數(shù)中的refresh()
調(diào)用。在這個方法中,通過createBeanFactory()
構(gòu)建了一個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 {
/// 這里判斷,如果已經(jīng)建立了BeanFactory,則銷毀并關(guān)閉該BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 這里是創(chuàng)建并設(shè)置持有的DefaultListableBeanFactory的地方
// 同時調(diào)用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)
方法,此方法中完成了資源的定位和加載,主要執(zhí)行如下代碼:
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
我們可以看到在AbstractRefreshableApplicationContext
抽象類中它只是提供了一個抽象的模板方法,具體實現(xiàn)由不同的子類去覆蓋實現(xiàn),這邊我們主要是通過FileSystemXmlApplicationContext
為例分析,所以我們找到相關(guān)的實現(xiàn)是在AbstractXmlApplicationContext
中去實現(xiàn)的:
/**
* 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.
// 為相應(yīng)的BeanFactory創(chuàng)建XmlBeanDefinitionReader(后續(xù)xml元信息的解析就依靠此類完成)
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 環(huán)境變量設(shè)置
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 設(shè)置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)
方法中實現(xiàn)了真正的資源定位和加載:
/**
* 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 {
// 獲取子類覆蓋實現(xiàn)的資源
Resource[] configResources = getConfigResources();
// 加載資源
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 獲取子類中設(shè)置的資源數(shù)組的資源路徑URL
String[] configLocations = getConfigLocations();
//實現(xiàn)加載
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
從代碼中我們可以看到String[] configLocations = getConfigLocations();
資源路徑的獲取是在這邊調(diào)用的,如果子類覆蓋可此實現(xiàn),那么我們直接可以從子類中獲取到相應(yīng)的資源文件。反過來看我們從FileSystemXmlApplicationContext
中去看,在FileSystemXmlApplicationContext
中確實是覆蓋了getConfigLocations();
方法。