1.前言
2.spring原始碼obtainFreshBeanFactory()介紹
3.總結
1.前言
github原始碼地址(帶註釋):
https://github.com/su15967456...
我們上篇部落格對spring的核心方法有了一個大概的認知,從今往後的幾篇部落格,我們將會將這幾個方法進行深入地分析。
話不多說,先上圖。
今天我們要介紹的obtainFreshBeanFactory()方法,其主要功能就是:
1.建立容器物件 DefaultListableBeanFactory
2.載入各種配置檔案的屬性到當前工廠中,最重要的就是封裝成BeanDefinition
2.spring原始碼obtainFreshBeanFactory()介紹
接下來我們來分析一下這個方法,首先往這個方法裡點選:
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化BeanFactory,並進行xml檔案讀取,並將得到的BeanFactory記錄在當前實體的屬性中
refreshBeanFactory();
return getBeanFactory();//返回當前實體的beanFactory屬性
}
註釋上告訴我們:主要就是生成一個bean工廠,我們可以繼續觀察refreshBeanFactory()方法。
/**
* 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 {
//如果有bean工廠了,先銷燬掉
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//建立DefaultListableBeanFactory物件
DefaultListableBeanFactory beanFactory = createBeanFactory();
//每個容器都有自己的id,為了序列化指定id,可以從id反序列化到beanFactory物件
beanFactory.setSerializationId(getId());
//定製beanFactory,設定相關屬性,包括是否允許覆蓋同名稱的不同定義的物件以及迴圈依賴,可以透過子類重寫
customizeBeanFactory(beanFactory);
//初始化documentReader,並進行對xml檔案進行解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我們可以一目瞭然地看到,這個方法的前幾步是十分簡單而明瞭的:
1.建立一個bean工廠物件
2.設定一下容器的id
3.設定一下beanFactory的相關屬性(包括是否允許覆蓋同名稱的不同定義的物件以及迴圈依賴,可以透過子類重寫) 如果不清楚括號內容也沒關係,主要就是有些屬性以後要用到,我們這裡要先進行一下初始化
4.對xml檔案進行解析(這是一個封裝方法,我們還要往裡面繼續檢視,看看spring是如何將xml檔案讀取到容器中的)
我們繼續debug,進入loadBeanDefinitions(beanFactory)方法;
/**
* 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.
//介面卡模式
//建立一個xml的beanDefinitionReader,並透過回撥設定到beanFactory中
//beanFactory和applicationContext沒有辦法直接讀取xml,就交給beanDefinitionReader進行,這就是介面卡模式
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 給reader物件設定環境物件
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//設定一個entity,用它來讀取本地的xsd或者dtd檔案,來完成相關的解析工作
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//設計模式,介面卡模式
//初始化beanDefinitionReader物件,此處設定配置檔案是否需要驗證
initBeanDefinitionReader(beanDefinitionReader);
//開始完成beanDefinition的載入
loadBeanDefinitions(beanDefinitionReader);
}
可以看出,大概進行了如下的操作:
建立一個XmlBeanDefinitionReader,透過XmlBeanDefinitionReader來完成beanDefinition的載入
這裡使用了介面卡模式:就是beanFactory本身沒辦法進行xml檔案(配置檔案)的讀取,所以要藉助beanDefinitionReader類進行對配置檔案的讀取,要讓beanDefinitionReader做一個適配。(就像手機沒辦法直接從插座中獲取電源,要藉助介面卡來充電。)
所以把beanFactory交給beanDefinitionReader,讓beanDefinitionReader讀取檔案,封裝成beanDefinition,加入beanFactory容器中。
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 此處獲取xml檔案的document物件,這個解析過程是由documentLoader完成的,
// 從string[] -> StringResources -> resources
// 最終將resources解析成一個個document文件,根據文件資訊封裝成BeanDefinition物件
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} 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);
}
}
然後我們一直往loadBeanDefinitions(beanDefinitionReader);裡debug,會發現兩個方法:
1.doLoadDocument(inputSource, resource);
2.registerBeanDefinitions(doc, resource);
注意,spring裡面do開頭的方法才是做實事的方法
我們來看doLoadDocument(inputSource, resource):
它主要做了兩件事
1)將resources解析成一個個document文件
2)根據這個文件資訊封裝成BeanDefinition物件
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 此處獲取xml檔案的document物件,這個解析過程是由documentLoader完成的,
// 從string[] -> StringResources -> resources
// 最終將resources解析成一個個document文件,根據文件資訊封裝成BeanDefinition物件
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} 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);
}
}
我們看看 registerBeanDefinitions(Document doc, Resource resource) 方法:
它也主要做兩件事情
1)對xml的封裝成的Document進行解析(Document->封裝成BeanDefinition)
2)完成BeanDefinition的註冊
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//對xml的beanDefinition進行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();//獲取當前bean的數量
//完成具體的解析過程
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
//獲得解析器
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);//根據根節點利用解析器解析元素
postProcessXml(root);
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) { //解析
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {//預設的名稱空間,import,bean,alias這些,可以解析,如果是其它標籤,需要額外的解析器
parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
3.總結
今天我們大概總結了一下obtainFreshBeanFactory()方法,該方法主要有兩個作用:
1.建立容器物件 DefaultListableBeanFactory
2.載入各種配置檔案的屬性到當前工廠中,封裝成BeanDefinition