Spring 4.x BeanFactory 原始碼分析
阿新 • • 發佈:2019-02-16
我們在之前的文章簡單的介紹了BeanFactory,現在看下原始碼。
啟動
我們看下之前的程式碼
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); // 獲取xml檔案。生成Resource Resource res = resolver.getResource("classpath:resource/spring-config.xml"); // 建立DefaultListableBeanFactory物件 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // 初始化BeanFactory reader.loadBeanDefinitions(res);
Resource —— getResource
這裡實現的是:PathMatchingResourcePatternResolver類的getResource
/** * Return the ResourceLoader that this pattern resolver works with. */ public ResourceLoader getResourceLoader() { return this.resourceLoader; } @Override public Resource getResource(String location) { return getResourceLoader().getResource(location); }
new DefaultListableBeanFactory();
/** * Create a new DefaultListableBeanFactory. */ public DefaultListableBeanFactory() { super(); } /** * Create a new AbstractAutowireCapableBeanFactory. */ public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); } /** * Create a new AbstractBeanFactory. */ public AbstractBeanFactory() { }
我們可以看到不僅僅是建立了一個DefaultListableBeanFactory物件,同時還建立了:AbstractAutowireCapableBeanFactory,AbstractBeanFactory兩個物件。
new XmlBeanDefinitionReader(factory);
/**
* Create new XmlBeanDefinitionReader for the given bean factory.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
// 父類
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// Determine ResourceLoader to use.
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
loadBeanDefinitions
/**
* Load bean definitions from the specified XML file.
* @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
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
下面是過載的方法
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
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());
}
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 {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
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();
}
}
}
其實可以看成兩部分程式碼
第一部分:
// 如果資原始檔不是空的話,
Assert.notNull(encodedResource, "EncodedResource must not be null");
// 判斷是否啟用了資訊日誌記錄
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 將資源設定為當前Resource
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 {
// 將Resource還原為InputStream流。
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 再將InputStream流轉換為InputSource — 對XML實體的一個單個輸入源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 核心方法:doLoadBeanDefinitions。
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
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();
}
}
doLoadBeanDefinitions
/**
* Actually load bean definitions from the specified XML file.
* 實際上從指定的XML檔案載入bean定義。
* @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 {
Document doc = doLoadDocument(inputSource, resource);
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);
}
}
其中核心程式碼為:
// 將資原始檔轉成Document物件
Document doc = doLoadDocument(inputSource, resource);
// 將Document物件註冊成為Bean。
return registerBeanDefinitions(doc, resource);
看下Document物件:
Document
我們看下Document介面介紹
package org.w3c.dom;
/**
* The <code>Document</code> interface represents the entire HTML or XML
* document. Conceptually, it is the root of the document tree, and provides
* the primary access to the document's data.
*
* 翻譯:
* Document介面代表全部的 HTML 或者 XML檔案。
* 概念上,他是整個檔案樹的根,同時給檔案的資料提供最主要的入口。
*/
同時要確定一點,Document介面是W3C的,並不是Spring的。
看下registerBeanDefinitions方法:
registerBeanDefinitions
/**
* Register the bean definitions contained in the given DOM document.
* 註冊包含在給定DOM文件中的bean定義。
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* 。。。
* 返回發現bean定義的數目
* @return the number of bean definitions found
* 。。。
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 獲取BeanDefinitionDocumentReader物件
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 返回登錄檔中定義的bean的數目。
int countBefore = getRegistry().getBeanDefinitionCount();
// 更新BeanDefinitionDocumentReader物件
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
兩個主要的方法:createBeanDefinitionDocumentReader
和registerBeanDefinitions
createBeanDefinitionDocumentReader
/**
* Create the {@link BeanDefinitionDocumentReader} to use for actually
* reading bean definitions from an XML document.
* <p>The default implementation instantiates the specified "documentReaderClass".
* @see #setDocumentReaderClass
* 翻譯:
* 建立BeanDefinitionDocumentReader,用於實際讀取XML文件中的bean定義。
* <P>預設實現例項化指定的“DopeCudieCrar類”。
*/
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
registerBeanDefinitions
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
* 翻譯:
* 此實現根據“Spring Bean”XSD(或DTD,歷史上)解析bean定義。
* <p>開啟DOM文件;然後初始化在該級別指定的預設設定;然後解析包含的bean定義。
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}