1.通過以下代碼分析
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Test.xml"));
通過XmlBeanFactory初始化時(shí)序圖,看看上面代碼的執(zhí)行邏輯

?在測(cè)試的BeanFactoryTest中首先調(diào)用ClassPathResource的構(gòu)造函數(shù)來構(gòu)造Resource資源文件的實(shí)例對(duì)象,后續(xù)的資源處理可以用Resource提供的各種服務(wù)操作,有了Resource之后就可以進(jìn)行XmlBeanFactory的初始化了。
2.配置文件的封裝
?在Java中,將不同來源的資源抽象成URL,通過注冊(cè)不同的handle(URLStreamHandler)來處理不同來源的資源的讀取邏輯,一般handle的類型使用不同的前綴(協(xié)議,Protocol)來識(shí)別,如“file”,“http”等,然而URL沒有默認(rèn)定義相會(huì)Classpath或者ServletContext等資源的handle,雖然可以注冊(cè)自己的Handler來解析但是需要了解URL的實(shí)現(xiàn)機(jī)制,而且URL也沒有提供一些基本的方法,如檢查當(dāng)前資源是不是存在,可讀的方法。
Spring對(duì)其內(nèi)部使用到的資源進(jìn)行了自己的抽象結(jié)構(gòu):Resource接口來封裝底層資源。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
//是否存在
boolean exists();
//是否可讀
boolean isReadable();
//是否處于打開的狀態(tài)
boolean isOpen();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
//根據(jù)當(dāng)前資源創(chuàng)建一個(gè)相對(duì)資源
Resource createRelative(String relativePath) throws IOException;'
String getFilename();
//在錯(cuò)誤處理中的打印信息
String getDescription();
}
?InputStreamSource封裝任何能返回InputStream的類,比如File,Classpath下的資源和Byte Array等。
?Resource接口抽象了所有Spring內(nèi)部使用到的底層資源:File,URL,ClassPath等。首先,定義了3個(gè)用于判斷當(dāng)前資源狀態(tài)的方法。另外還提供了不同資源到URL,URI,F(xiàn)ile類型的轉(zhuǎn)換,以及獲取lastModified屬性,文件名的方法。還提供了基于當(dāng)前資源創(chuàng)建一個(gè)相對(duì)資源的方法;createRelative()。
?對(duì)于不同來源的資源文件都有相應(yīng)的Resource實(shí)現(xiàn):羅列部分。

?對(duì)于實(shí)現(xiàn)的方式很簡(jiǎn)單,以getInputStream為例,ClassPathResource中實(shí)現(xiàn)的方式是通過class或者classLoader提供的底層方法進(jìn)行調(diào)用,而對(duì)于FileSystemResource的實(shí)現(xiàn)直接使用FileInputStream對(duì)文件進(jìn)行實(shí)例化。
//ClassPathResource.java中的getInputStream
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
FileSystemResource.java 中的getInputStream
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
通過Resource相關(guān)類完成了對(duì)配置文件進(jìn)行了封裝后配置文件的讀取工作就全權(quán)交給XmlBeanDefinitionReader處理了。
3.XmlBeanFactory的初始化過程:
XmlBeanFactory的初始化有若干辦法,Spring提供了很多的構(gòu)造函數(shù),代碼如下:
public class XmlBeanFactory extends DefaultListableBeanFactory {
//獲取XmlBeanDefinitionReader用于加載resource資源用
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
//調(diào)用XmlBeanFactory(Resource,BeanFactory)構(gòu)造方法
this(resource, null);
}
//parentBeanFactory為父類BeanFactory用于factory合并,可以為空
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//這里才是加載資源的正真實(shí)現(xiàn)
this.reader.loadBeanDefinitions(resource);
}
}
?在調(diào)用加載資源文件之前還有一個(gè)調(diào)用父類構(gòu)造器初始化的過程:super(parentBeanFactory),跟蹤代碼到父類AbstractAutowireCapableBeanFactory構(gòu)造器
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
?其中ignoreDependencyInterface方法的主要功能是忽略給定接口的自動(dòng)裝備功能,這樣做的目的是:
?如果A中有屬性B,那么當(dāng)Spring在獲取A的Bean的時(shí)候如果其屬性B還沒有初始化,那么Spring就會(huì)自動(dòng)初始化B,這也是Spring中提供的一個(gè)重要特性。
?但是,某些情況下,B不會(huì)被初始化,其中的一種情況就是B實(shí)現(xiàn)了BeanNameAware接口。Spring中是這樣介紹的:自動(dòng)裝配時(shí)忽略給定的依賴接口,典型應(yīng)用就是通過其他方式解析Application上下文注冊(cè)依賴,類似于BeanFactory通過BeanNameAware進(jìn)行注冊(cè)或者ApplicationContext通過ApplicationContextAware進(jìn)行注入。
4.加載Bean

從 this.reader.loadBeanDefinitions(resource)這行代碼作為切入點(diǎn)。
(1)封裝資源文件:當(dāng)進(jìn)入XmlBeanDefinitionReader后首先對(duì)參數(shù)Resource使用EncodedResource類進(jìn)行封裝
(2)獲取輸入流。從Resource中獲取對(duì)應(yīng)的InputStream并構(gòu)造InputSource
(3)通過構(gòu)造的InputSource實(shí)例和Resource實(shí)例繼續(xù)調(diào)用函數(shù)doLoadBeanDefinition。
具體的實(shí)現(xiàn)過程:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
EncodedResource這個(gè)類樹妖適用于對(duì)資源文件的編碼進(jìn)行處理。其中主要邏輯體現(xiàn)在類中的getReader()方法中,當(dāng)設(shè)置了編碼屬性的時(shí)候Spring會(huì)使用相應(yīng)的編碼作為輸入流的編碼
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
上面代碼構(gòu)造了一個(gè)有編碼的inputStreamReader。當(dāng)構(gòu)造好encodedResource對(duì)象后,再次轉(zhuǎn)入了可服用方法LoadBeanDefinition(new EncodedResource(resource))。
這個(gè)方法內(nèi)部才是正真的數(shù)據(jù)準(zhǔn)備階段;
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//通過屬性來記錄已經(jīng)加載過的資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//從encodedResource中獲取已經(jīng)編碼封裝的Resource對(duì)象并再次從Resource中獲取inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//InputSource這個(gè)類并不是來自Spring,他的全路徑時(shí)prg.xml.sax.inputStream
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//進(jìn)入邏輯核心部分
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//關(guān)閉輸入流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
再次整理一次,首先對(duì)傳入的resource參數(shù)做封裝,目的是考慮到Resource可能存在編碼要求的情況,其次,通過SAX讀取XML文件的方式來準(zhǔn)備InputSource對(duì)象,最后將準(zhǔn)備的數(shù)據(jù)通過參數(shù)傳入正真的核心處理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource());
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//獲取對(duì)XML文件的驗(yàn)證模式
int validationMode = getValidationModeForResource(resource);
//加載XML文件,并得到對(duì)應(yīng)的Document
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
//根絕返回的Document注冊(cè)Bean信息
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
在這個(gè)方法里面主要做了三件事;
(1)獲取對(duì)XML文件的驗(yàn)證模式
(2)加載XML文件,并的到對(duì)應(yīng)的Document
(3)根據(jù)返回的Document注冊(cè)Bean信息