Spring XML Bean 定義的加載和註冊

前言

本篇文章主要介紹 Spring IoC 容器怎麼加載 bean 的定義元信息。

下圖是一個大致的流程圖:

Spring XML Bean 定義的加載和註冊

第一次畫圖,畫的有點爛。:joy:

正文

首先定義一個簡單的 POJO,如下:

<code>public class User {

\tprivate Long id;
\tprivate String name;

\tpublic Long getId() {
\t\treturn id;
\t}

\tpublic void setId(Long id) {
\t\tthis.id = id;
\t}

\tpublic String getName() {
\t\treturn name;
\t}

\tpublic void setName(String name) {
\t\tthis.name = name;
\t}

\t@Override
\tpublic String toString() {
\t\treturn "User{" +
\t\t\t\t"id=" + id +
\t\t\t\t", name='" + name + '\\'' +
\t\t\t\t'}';
\t}
}/<code>

再編寫一個 XML 文件。

<code>
<beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

\t<bean>
\t\t<property>

\t\t<property>
\t/<bean>

/<beans>/<code>

最後再來一個測試類。

<code>@Test
public void test(){
\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
\tXmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
\treader.loadBeanDefinitions("META-INF/spring-bean.xml");
\tUser user = beanFactory.getBean("user", User.class);
\tSystem.out.println(user);
}/<code>

上面這段代碼比較簡單,無非就是聲明 bean 工廠,然後通過指定的 XML 文件加載 bean 的定義元信息,最後通過 bean 工廠獲取 bean 。

DefaultListableBeanFactory

首先我們來了解一下 DefaultListableBeanFactory ,下面是該類的類圖及層次結構。

Spring XML Bean 定義的加載和註冊

  • AliasRegistry: 定義對 alias 的簡單增刪改等操作。
  • SimpleAliasRegistry: 主要使用 map 作為 alias 的緩存,並對接口 AliasRegistry 進行實現。
  • SingletonBeanRegistry: 定義了對單例 bean 的註冊及獲取。
  • BeanFactory: 定義獲取單個 bean 及 bean 的各種屬性。
  • DefaultSingletonBeanRegistry: 對接口 SingletonBeanRegistry 各函數的實現。
  • HierarchicalBeanFactory: 繼承 BeanFactory ,也就是在 BeanFactory 定義的功能的基礎上增加了對 parentBeanFactory 的支持。
  • BeanDefinitionRegistry: 定義了對 BeanDefinition 的各種增刪改操作。
  • FactoryBeanRegistrySupport: 在 DefaultSingletonBeanRegistry 基礎上增加了對 FactoryBean 的特殊處理功能。
  • ConfigurableBeanFactory:
    提供配置 BeanFactory 的各種方法。
  • ListableBeanFactory: 繼承 BeanFactory 提供了獲取多個 bean 的各種方法。
  • AbstractBeanFactory: 綜合 FactoryBeanRegistrySupport 和 ConfigurableBeanFactory 的功能。
  • AutowireCapableBeanFactory: 提供創建 bean 、自動注入、初始化以及應用 bean 的後處理器。
  • AbstractAutowireCapableBeanFactory: 綜合 AbstractBeanFactory 並對接口 AutowireCapableBeanFactory 進行實現。
  • ConfigurableListableBeanFactory: BeanFactory 配置清單,指定忽略類型及接口等。
  • DefaultListableBeanFactory: 綜合上面所有功能,主要是對 bean 註冊後的處理。

可以看到上面的接口大多數是定義了一些功能或在父接口上擴展了一些功能, DefaultListableBeanFactory 實現了所有接口,大多數默認情況下我們所使用的 beanFactory 就是 DefaultListableBeanFactory 。

1.AbstractBeanDefinitionReader#loadBeanDefinitions 方法

<code>public int loadBeanDefinitions(String location, @Nullable Set<resource> actualResources) throws BeanDefinitionStoreException {
// 獲取 resourceLoader,這邊是 PathMatchingResourcePatternResolver

\tResourceLoader resourceLoader = getResourceLoader();
\tif (resourceLoader == null) {
\t\tthrow new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
\t}
\t// 判斷 resourceLoader 是否是 ResourcePatternResolver,我們這邊是符合的
\tif (resourceLoader instanceof ResourcePatternResolver) {
\t\t// Resource pattern matching available.
\t\ttry {
// 根據路徑獲取所欲符合的配置文件並封裝成 Resource 對象
\t\t\tResource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 根據 Resource 加載 bean definition,並返回數量,見下面詳解
\t\t\tint count = loadBeanDefinitions(resources);
\t\t\tif (actualResources != null) {
\t\t\t\tCollections.addAll(actualResources, resources);
\t\t\t}
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
\t\t\t}
\t\t\treturn count;
\t\t}
\t\tcatch (IOException ex) {
\t\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t\t"Could not resolve bean definition resource pattern [" + location + "]", ex);
\t\t}
\t}
\telse {
\t\t// Can only load single resources by absolute URL.
// 只能通過絕對路徑加載單個資源
\t\tResource resource = resourceLoader.getResource(location);
// 根據 Resource 加載 bean definition,並返回數量,見下面詳解
\t\tint count = loadBeanDefinitions(resource);
\t\tif (actualResources != null) {
\t\t\tactualResources.add(resource);
\t\t}
\t\tif (logger.isTraceEnabled()) {
\t\t\tlogger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
\t\t}
\t\treturn count;
\t}
}/<resource>/<code>

上面方法主要是將資源文件轉換為 Resource 對象,然後調用 loadBeanDefinitions(Resource...) 加載 BeanDefinition 。

<code>public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
\tAssert.notNull(resources, "Resource array must not be null");
\tint count = 0;
\tfor (Resource resource : resources) {
// 加載 BeanDefinition,見下文詳解
\t\tcount += loadBeanDefinitions(resource);
\t}
\treturn count;
}/<code>

該方法主要就是遍歷 resources 然後調用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource) 。

2.XmlBeanDefinitionReader#loadBeanDefinitions

<code>public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 將 Resource 封裝成 EncodedResource,也就是對資源指定編碼和字符集
\treturn loadBeanDefinitions(new EncodedResource(resource));
}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
\tAssert.notNull(encodedResource, "EncodedResource must not be null");
\tif (logger.isTraceEnabled()) {
\t\tlogger.trace("Loading XML bean definitions from " + encodedResource);
\t}
\t// 當前正在加載的 EncodedResource
\tSet<encodedresource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
\tif (currentResources == null) {
\t\tcurrentResources = new HashSet<>(4);
\t\tthis.resourcesCurrentlyBeingLoaded.set(currentResources);
\t}
// 如果當前 EncodedResource 以及存在,代表出現了循環加載,拋出異常
\tif (!currentResources.add(encodedResource)) {
\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
\t}
\ttry {
// 獲取 Resource 的輸入流
\t\tInputStream inputStream = encodedResource.getResource().getInputStream();
\t\ttry {
// 將 inputStream 封裝成 org.xml.sax.InputSource

\t\t\tInputSource inputSource = new InputSource(inputStream);
// 如果 encodedResource 的編碼不為空,設置 inputSource 的編碼
\t\t\tif (encodedResource.getEncoding() != null) {
\t\t\t\tinputSource.setEncoding(encodedResource.getEncoding());
\t\t\t}
// 加載 BeanDefinition (方法以 do 開頭,真正處理的方法)
\t\t\treturn doLoadBeanDefinitions(inputSource, encodedResource.getResource());
\t\t}
\t\tfinally {
// 關閉流
\t\t\tinputStream.close();
\t\t}
\t}
\tcatch (IOException ex) {
\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t"IOException parsing XML document from " + encodedResource.getResource(), ex);
\t}
\tfinally {
// 當前資源以及加載完畢,從 currentResources 中移除
\t\tcurrentResources.remove(encodedResource);
\t\tif (currentResources.isEmpty()) {
\t\t\tthis.resourcesCurrentlyBeingLoaded.remove();
\t\t}
\t}
}/<encodedresource>/<code>

上面主要將 Resource 封裝成 EncodedResource ,也就是制定資源的編碼和字符集。然後獲取 Resource 的輸入流 InputStream ,並封裝成 InputSource 設置其編碼,最終調用 doLoadBeanDefinitions 開始真正的加載流程。

3.XmlBeanDefinitionReader#doLoadBeanDefinitions

<code>protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
\t\tthrows BeanDefinitionStoreException {
\ttry {
// 根據 inputSource 和 resource 加載 XML 文件,並封裝成 Document,見下文詳解
\t\tDocument doc = doLoadDocument(inputSource, resource);
// 用 doc 去解析和註冊 bean definition,見下文詳解
\t\tint count = registerBeanDefinitions(doc, resource);

\t\tif (logger.isDebugEnabled()) {
\t\t\tlogger.debug("Loaded " + count + " bean definitions from " + resource);
\t\t}
\t\treturn count;
\t}
\tcatch (BeanDefinitionStoreException ex) {
\t\tthrow ex;
\t}
\tcatch (SAXParseException ex) {
\t\tthrow new XmlBeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
\t}
\tcatch (SAXException ex) {
\t\tthrow new XmlBeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"XML document from " + resource + " is invalid", ex);
\t}
\tcatch (ParserConfigurationException ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Parser configuration exception parsing XML from " + resource, ex);
\t}
\tcatch (IOException ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"IOException parsing XML document from " + resource, ex);
\t}
\tcatch (Throwable ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Unexpected exception parsing XML document from " + resource, ex);
\t}
}/<code>

上面代碼拋開異常處理,邏輯非常簡單,就是用 inputSource 和 resource 加載 XML 文件,並封裝成 Document 對象,然後去註冊 BeanDefinition 。

4.XmlBeanDefinitionReader#doLoadDocument

<code>protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
\treturn this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
\t\t\tgetValidationModeForResource(resource), isNamespaceAware());
}

// 獲取 XML 文件的驗證模式
protected int getValidationModeForResource(Resource resource) {
// 如果手動指定了驗證模式則使用指定的驗證模式
\tint validationModeToUse = getValidationMode();

\tif (validationModeToUse != VALIDATION_AUTO) {
\t\treturn validationModeToUse;
\t}
// 如果未指定則使用自動檢測,其實就是判斷文件是否包含 DOCTYPE,如果
\tint detectedMode = detectValidationMode(resource);
\tif (detectedMode != VALIDATION_AUTO) {
\t\treturn detectedMode;
\t}
\t// Hmm, we didn't get a clear indication... Let's assume XSD,
\t// since apparently no DTD declaration has been found up until
\t// detection stopped (before finding the document's root tag).
// 如果沒有找到驗證,默認使用 XSD 模式,因為 DTD 已經不維護了
\treturn VALIDATION_XSD;
}

// DefaultDocumentLoader.java
// 這裡就是使用 DocumentLoader 去加載 XML 文件。首先創建 DocumentBuilderFactory,再通過 DocumentBuilderFactory 創建 DocumentBuilder,進而解析 inputSource 來返回 Document 對象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
\t\t\tErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

\tDocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
\tif (logger.isTraceEnabled()) {
\t\tlogger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
\t}
\tDocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
\treturn builder.parse(inputSource);
}/<code>

detectValidationMode() 方法其實就是讀取文件內容,判斷是否包含 DOCTYPE ,如果包含就是 DTD 否則就是 XSD。

獲取 XML 配置文件的驗證模式。XML 文件的驗證模式是用來保證 XML 文件的正確性,常見的驗證模式有 DTD 和 XSD。

DTD XML 格式示例:

Spring XML Bean 定義的加載和註冊

STD XML 格式示例:

Spring XML Bean 定義的加載和註冊

5.XmlBeanDefinitionReader#registerBeanDefinitions

<code>public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 獲取 DefaultBeanDefinitionDocumentReader
\tBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 獲取註冊中心,再靠註冊中心獲取註冊之前以及註冊過的 BeanDefinition 數量
\tint countBefore = getRegistry().getBeanDefinitionCount();
// 解析並註冊 BeanDefinition,見下文詳情
\tdocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 獲取註冊過後 BeanDefinition 數量減去註冊之前的數量,得到的就是本次註冊的數量
\treturn getRegistry().getBeanDefinitionCount() - countBefore;
}/<code>

這裡的 getRegistry() 方法返回的就是 DefaultListableBeanFactory ,因為就只有它實現了 BeanDefinitionRegistry 接口。

DefaultListableBeanFactory 中定義了存放 BeanDefinition 的緩存,所以 getBeanDefinitionCount() 方法返回的就是 beanDefinitionMap 的數量。

<code>// 存放 BeanDefinition 的緩存,key 為 bean 的名稱,value 就是其 BeanDefinition
private final Map<string> beanDefinitionMap = new ConcurrentHashMap<>(256);/<string>/<code>

6.DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions

<code>public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
\tthis.readerContext = readerContext;
// 提取 root,註冊 BeanDefinition (理論上 Spring 的配置文件,root 都應該是 beans 標籤)
\tdoRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
\t// Any nested <beans> elements will cause recursion in this method. In
\t// order to propagate and preserve <beans> default-* attributes correctly,
\t// keep track of the current (parent) delegate, which may be null. Create
\t// the new (child) delegate with a reference to the parent for fallback purposes,

\t// then ultimately reset this.delegate back to its original (parent) reference.
\t// this behavior emulates a stack of delegates without actually necessitating one.
\tBeanDefinitionParserDelegate parent = this.delegate;
// 專門處理解析
\tthis.delegate = createDelegate(getReaderContext(), root, parent);
\t// 校驗 root 節點的命名空間是否為默認的命名空間 (默認命名空間http://www.springframework.org/schema/beans)
\tif (this.delegate.isDefaultNamespace(root)) {
// 處理 profile 屬性
\t\tString profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
\t\tif (StringUtils.hasText(profileSpec)) {
\t\t\tString[] specifiedProfiles = StringUtils.tokenizeToStringArray(
\t\t\tprofileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
\t\t\t// We cannot use Profiles.of(...) since profile expressions are not supported
\t\t\t// in XML config. See SPR-12458 for details.
// 校驗當前節點的 profile 是否符合當前環境定義的,如果不是則直接跳過,不解析該節點下的內容
\t\t\tif (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
\t\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\t\tlogger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
\t\t\t\t\t\t\t"] not matching: " + getReaderContext().getResource());
\t\t\t\t}
\t\t\t\treturn;
\t\t\t}
\t\t}
\t}
\t// 解析前處理,留給子類實現
\tpreProcessXml(root);
// 解析註冊 BeanDefinition,見下文詳解
\tparseBeanDefinitions(root, this.delegate);
// 解析後處理,留給子類實現
\tpostProcessXml(root);

\tthis.delegate = parent;
}/<beans>/<beans>/<code>

profile 主要是用於多環境開發,例如:

Spring XML Bean 定義的加載和註冊

集成到 Web 環境時,在 web.xml 中加入以下代碼:

<code><coontext-param>
\t<param-name>Spring.profiles.active/<param-name>
<param-value>dev/<param-value>
/<coontext-param>/<code>

preProcessXml() 和 postProcessXml() 採用的 模板方法模式 ,子類可以繼承 DefaultBeanDefinitionDoucumentReader 來重寫這兩個方法,這也是解析前後的擴展點。

7.DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions

<code>protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 校驗 root 節點的命名空間是否為默認的命名空間,這裡為什麼再次效驗,因為調用解析前調用了preProcessXml() 方法,可能會對節點做修改
\tif (delegate.isDefaultNamespace(root)) {
\t\tNodeList nl = root.getChildNodes();
\t\tfor (int i = 0; i < nl.getLength(); i++) {
\t\t\tNode node = nl.item(i);
\t\t\tif (node instanceof Element) {
\t\t\t\tElement ele = (Element) node;
\t\t\t\tif (delegate.isDefaultNamespace(ele)) {
// 默認命名空間節點的處理,例如 <bean>
\t\t\t\t\tparseDefaultElement(ele, delegate);
\t\t\t\t}
\t\t\t\telse {
// 自定義命名空間節點的處理,例如 <compoent-scan>、<aspectj-autoproxy>
\t\t\t\t\tdelegate.parseCustomElement(ele);
\t\t\t\t}
\t\t\t}
\t\t}
\t}
\telse {
// 自定義命名空間節點的處理

\t\tdelegate.parseCustomElement(root);
\t}
}


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 對 import 標籤的處理
\tif (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
\t\timportBeanDefinitionResource(ele);
\t}
// 對 alias 標籤的處理
\telse if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
\t\tprocessAliasRegistration(ele);
\t}
// 對 bean 標籤的處理
\telse if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
\t\tprocessBeanDefinition(ele, delegate);
\t}
// 對 beans 標籤的處理
\telse if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
\t\tdoRegisterBeanDefinitions(ele);
\t}
}/<aspectj-autoproxy>/<code>

上面 parseDefaultElement 方法中對 bean 標籤的處理方法 processBeanDefinition 最為重要,下面來著重分析一下。

7-1.DefaultBeanDefinitionDocumentReader#processBeanDefinition

<code>protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將 ele 解析成 BeanDefinitionHolder,見下面詳解
\tBeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
\tif (bdHolder != null) {
// 若存在默認標籤下的子節點下不再有自定義屬性,需要再次對自定義標籤再進行解析(基本不用,不做深入分析)
\t\tbdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
\t\ttry {
\t\t\t// Register the final decorated instance.
// 註冊最終的 BeanDefinition
\t\t\tBeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
\t\t}

\t\tcatch (BeanDefinitionStoreException ex) {
\t\t\tgetReaderContext().error("Failed to register bean definition with name '" +
\t\t\t\t\t\tbdHolder.getBeanName() + "'", ele, ex);
\t\t}
\t\t// Send registration event.
// 發出響應事件,通知相關監聽器,這個 bean 已經註冊完
\t\tgetReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
\t}
}/<code>

上面代碼主要步驟如下:

  1. 將 Element 解析成 BeanDefinitionHolder 。
  2. 若存在默認標籤下的子節點下有自定義屬性,需要再次對自定義標籤再進行解析。
  3. 註冊 BeanDefinition 。
  4. 發出響應事件,通知相關監聽器,這個 bean 已經註冊完,具體詳情可以查看 ReaderEventListener#componentRegistered() 方法。可以通過以下方式去註冊這個監聽器:
Spring XML Bean 定義的加載和註冊

7-1-1.BeanDefinitionParseDelegate#parseBeanDefinitionElement

<code>public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
// 解析元素,封裝成 BeanDefinitionHolder
\treturn parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 獲取 id 屬性
\tString id = ele.getAttribute(ID_ATTRIBUTE);
// 獲取 name 屬性
\tString nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
\tList<string> aliases = new ArrayList<>();
// 將 name 屬性所有的名稱按照逗號或者分號(,;)分割成數組放入別名集合 aliases
\tif (StringUtils.hasLength(nameAttr)) {
\t\tString[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
\t\taliases.addAll(Arrays.asList(nameArr));
\t}
\t// beanName 默認使用 id
\tString beanName = id;
// 沒有指定 id 屬性 && 指定了 name 屬性
\tif (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果沒有指定id,beanName 等於第一個別名,剩下的依然作為別名使用
\t\tbeanName = aliases.remove(0);
\t\tif (logger.isTraceEnabled()) {
\t\t\tlogger.trace("No XML 'id' specified - using '" + beanName +
\t\t\t\t\t"' as bean name and " + aliases + " as aliases");
\t\t}
\t}

\tif (containingBean == null) {
// 驗證 beanName 和 aliases 是否在同一個 <beans> 下已經存在
\t\tcheckNameUniqueness(beanName, aliases, ele);
\t}
\t// 將元素解析成 GenericBeanDefinition
\tAbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
\tif (beanDefinition != null) {
// 如果不存在 beanName 會根據 Spring 的命名規則生成一個

\t\tif (!StringUtils.hasText(beanName)) {
\t\t\ttry {
\t\t\t\tif (containingBean != null) {
\t\t\t\t\tbeanName = BeanDefinitionReaderUtils.generateBeanName(
\t\t\t\t\t\t\tbeanDefinition, this.readerContext.getRegistry(), true);
\t\t\t\t}
\t\t\t\telse {
\t\t\t\t\tbeanName = this.readerContext.generateBeanName(beanDefinition);
\t\t\t\t\t// Register an alias for the plain bean class name, if still possible,
\t\t\t\t\t// if the generator returned the class name plus a suffix.
\t\t\t\t\t// This is expected for Spring 1.2/2.0 backwards compatibility.
\t\t\t\t\tString beanClassName = beanDefinition.getBeanClassName();
\t\t\t\t\tif (beanClassName != null &&
\t\t\t\t\t\t\tbeanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
\t\t\t\t\t!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
\t\t\t\t\t\t\taliases.add(beanClassName);
\t\t\t\t\t}
\t\t\t\t}
\t\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\t\tlogger.trace("Neither XML 'id' nor 'name' specified - " +
\t\t\t\t\t\t\t"using generated bean name [" + beanName + "]");
\t\t\t\t}
\t\t\t}
\t\t\tcatch (Exception ex) {
\t\t\t\terror(ex.getMessage(), ele);
\t\t\t\treturn null;
\t\t\t}
\t\t}
\t\tString[] aliasesArray = StringUtils.toStringArray(aliases);
// 用 beanDefinition 和 beanName 以及 aliasesArray 構建 BeanDefinitionHolder
\t\treturn new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
\t}
\treturn null;
}


public AbstractBeanDefinition parseBeanDefinitionElement(
\t\t\tElement ele, String beanName, @Nullable BeanDefinition containingBean) {

\tthis.parseState.push(new BeanEntry(beanName));

\tString className = null;
// 獲取 class 屬性
\tif (ele.hasAttribute(CLASS_ATTRIBUTE)) {
\t\tclassName = ele.getAttribute(CLASS_ATTRIBUTE).trim();
\t}
\tString parent = null;
// 獲取 parent 屬性
\tif (ele.hasAttribute(PARENT_ATTRIBUTE)) {

\t\tparent = ele.getAttribute(PARENT_ATTRIBUTE);
\t}

\ttry {
// 創建用於承載屬性的 AbstractBeanDefinition 類型的 GenericBeanDefinition
\t\tAbstractBeanDefinition bd = createBeanDefinition(className, parent);
\t\t// 解析默認 bean 的各種屬性,見下方 parseBeanDefinitionAttributes 方法詳解
\t\tparseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
\t\tbd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
\t\t// 解析元數據,見下方 parseMetaElements 方法詳解
\t\tparseMetaElements(ele, bd);
// 解析 lookup-method 屬性,很少使用,不具體介紹
\t\tparseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method 屬性,很少使用,不具體介紹
\t\tparseReplacedMethodSubElements(ele, bd.getMethodOverrides());
\t\t// 解析 constructot-arg 屬性,見下方 parseConstructorArgElements 方法詳解
\t\tparseConstructorArgElements(ele, bd);
// 解析 property 屬性,見下方 parsePropertyElements 方法詳解
\t\tparsePropertyElements(ele, bd);
// 解析 qualifier 屬性,見下方 parseQualifierElements 方法詳解
\t\tparseQualifierElements(ele, bd);
\t\t
\t\tbd.setResource(this.readerContext.getResource());
\t\tbd.setSource(extractSource(ele));

\t\treturn bd;
\t}
\tcatch (ClassNotFoundException ex) {
\t\terror("Bean class [" + className + "] not found", ele, ex);
\t}
\tcatch (NoClassDefFoundError err) {
\t\terror("Class that bean class [" + className + "] depends on not found", ele, err);
\t}
\tcatch (Throwable ex) {
\t\terror("Unexpected failure during bean definition parsing", ele, ex);
\t}
\tfinally {
\t\tthis.parseState.pop();

\t}

\treturn null;
}/<beans>/<string>/<code>

上面代碼主要將 bean 標籤,解析為 BeanDefinitionHolder 返回,主要步驟如下:

  1. 解析 id 、 name 屬性,將 name 按照 , 或者 ; 分割作為別名 ( alias )。
  2. 解析剩下的屬性,並封裝成 GenericBeanDefinition 。調用 parseBeanDefinitionAttributes 方法解析 bean 標籤的所有屬性。調用 parseMetaElements 方法解析元數據信息。調用 parseLookupOverrideSubElements 方法解析 lookup-method 子標籤。調用 parseReplacedMethodSubElements 方法解析 replaced-method 子標籤。調用 parseConstructorArgElements 方法解析 constructor-arg 子標籤。調用 parsePropertyElements 方法解析 property 子標籤。調用 parseQualifierElements 方法解析 qualifier 子標籤。
  3. 判斷 beanName 是否存在,不存在會根據 Spring 的命名規則生成一個。
  4. 使用 beanDefinition 、 beanName 、 aliasesArray 構建 BeanDefinitionHolder 返回。

我們這邊可以簡單看一下 BeanDefinitionHolder 的屬性,如下:

<code>public class BeanDefinitionHolder implements BeanMetadataElement {
\t
// bean 的定義元信息
\tprivate final BeanDefinition beanDefinition;
\t// bean 的名稱
\tprivate final String beanName;
\t// bean 的別名數組
\t@Nullable
\tprivate final String[] aliases;

...省略其它代碼

}/<code>

BeanDefinitionHolder 其實就是對 BeanDefinition 的包裝。

parseBeanDefinitionAttributes

<code>public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
\t\t\t@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
\t// 解析 singleton 屬性
\tif (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton 屬性已經不支持了,使用了會直接拋出異常,請使用 scope 屬性替代
\t\terror("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
\t}
// 解析 scope 屬性
\telse if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
\t\tbd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
\t}
\telse if (containingBean != null) {
\t\t// Take default from containing bean in case of an inner bean definition.
\t\tbd.setScope(containingBean.getScope());
\t}
\t// 解析 abstract 屬性
\tif (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
\t\tbd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
\t}
\t// 解析 lazy 屬性
\tString lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
// 若沒有設置或者設置成其他字符都會被設置為默認值 false
\tif (isDefaultValue(lazyInit)) {
\t\tlazyInit = this.defaults.getLazyInit();
\t}
\tbd.setLazyInit(TRUE_VALUE.equals(lazyInit));
\t// 解析 autowire 屬性
\tString autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
\tbd.setAutowireMode(getAutowireMode(autowire));
\t// 解析 depends-on 屬性
\tif (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
\t\tString dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
\t\tbd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
\t}
\t// 解析 autowire-candidate 屬性
\tString autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);

\tif (isDefaultValue(autowireCandidate)) {
\t\tString candidatePattern = this.defaults.getAutowireCandidates();
\t\tif (candidatePattern != null) {
\t\t\tString[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
\t\t\tbd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
\t\t}
\t}
\telse {
\t\tbd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
\t}
\t// 解析 primary 屬性
\tif (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
\t\tbd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
\t}
\t// 解析 init-mehtod 屬性
\tif (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
\t\tString initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
\t\tbd.setInitMethodName(initMethodName);
\t}
// 如果 beans 標籤指定了 default-init-method 屬性,則會給所有此標籤下的 bean 都指定該 init-method
\telse if (this.defaults.getInitMethod() != null) {
\t\tbd.setInitMethodName(this.defaults.getInitMethod());
\t\tbd.setEnforceInitMethod(false);
\t}
\t// 解析 destory-method 屬性
\tif (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
\t\tString destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
\t\tbd.setDestroyMethodName(destroyMethodName);
\t}
// 如果 beans 標籤指定了 default-destory-method 屬性,則會給所有此標籤下的 bean 都指定該 destory-method
\telse if (this.defaults.getDestroyMethod() != null) {
\t\tbd.setDestroyMethodName(this.defaults.getDestroyMethod());
\t\tbd.setEnforceDestroyMethod(false);
\t}
\t// 解析 factory-method 屬性
\tif (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
\t\tbd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
\t}
// 解析 factory-bean 屬性
\tif (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
\t\tbd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
\t}
\t
\treturn bd;

}/<code>

上面方法完成了對所有 bean 標籤屬性的解析。值得注意的地方是如果同時指定了 bean 標籤的 init-method 和 beans 標籤的 default-init-method 屬性,那麼優先使用前者, destory-mehtod 標籤也是一樣。

大家可以去看一下 AbstractBeanDefinition 中定義的屬性就一目瞭然了,這裡限於篇幅原因就不展示了。

parseMetaElements

這裡先回顧一下元數據 meta 屬性的使用。

Spring XML Bean 定義的加載和註冊

這個屬性並不會體現在 user 的屬性當中,而是一個額外的聲明,當需要使用裡面的信息時可以通過 BeanDefinition#getAttribute(key) 來獲取。

<code>public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
// 獲取所有子節點
\tNodeList nl = ele.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 提取 meta
\t\tif (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
\t\t\tElement metaElement = (Element) node;
\t\t\tString key = metaElement.getAttribute(KEY_ATTRIBUTE);
\t\t\tString value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 使用 key、value 構造 BeanMetadataAttribute
\t\t\tBeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
\t\t\tattribute.setSource(extractSource(metaElement));
// 記錄信息
\t\t\tattributeAccessor.addMetadataAttribute(attribute);
\t\t}
\t}
}/<code>

parseConstructorArgElements

<code>public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 獲取所有子節點
\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 提取 constructor-arg
\t\tif (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
\t\t\tparseConstructorArgElement((Element) node, bd);
\t\t}
\t}
}
// <constructor-arg>
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取 index 屬性
\tString indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取 type 屬性
\tString typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取 name 屬性

\tString nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// index 不為空
\tif (StringUtils.hasLength(indexAttr)) {
\t\ttry {
// 轉換為 int 類型
\t\t\tint index = Integer.parseInt(indexAttr);
\t\t\tif (index < 0) {
\t\t\t\terror("'index' cannot be lower than 0", ele);
\t\t\t}
\t\t\telse {
\t\t\t\ttry {
\t\t\t\t\tthis.parseState.push(new ConstructorArgumentEntry(index));
// 解析屬性值,見下面詳解
\t\t\t\t\tObject value = parsePropertyValue(ele, bd, null);
// 使用 ConstructorArgumentValues.ValueHolder 類型來封裝解析出來的元素
\t\t\t\t\tConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 如果 type 不為空,設置 ValueHolder 的 type
\t\t\t\t\tif (StringUtils.hasLength(typeAttr)) {
\t\t\t\t\t\tvalueHolder.setType(typeAttr);
\t\t\t\t\t}
// 如果 name 不為空,設置 ValueHolder 的 name
\t\t\t\t\tif (StringUtils.hasLength(nameAttr)) {
\t\t\t\t\t\tvalueHolder.setName(nameAttr);
\t\t\t\t\t}
\t\t\t\t\tvalueHolder.setSource(extractSource(ele));
// 判斷 index 是否重複,重複則拋出異常
\t\t\t\t\tif (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {\t\t\t\t\t\t\terror("Ambiguous constructor-arg entries for index " + index, ele);
\t\t\t\t\t}
// 將 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 當中
\t\t\t\t\telse {
\t\t\t\t\t\tbd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
\t\t\t\t\t}
\t\t\t\t}
\t\t\t\tfinally {
\t\t\t\t\tthis.parseState.pop();
\t\t\t\t}
\t\t\t}
\t\t}
\t\tcatch (NumberFormatException ex) {
\t\t\terror("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
\t\t}
\t}
\telse {

\t\ttry {
// 這裡就是 constructor-arg 標籤中沒有指定 index 屬性
\t\t\tthis.parseState.push(new ConstructorArgumentEntry());
\t\t\tObject value = parsePropertyValue(ele, bd, null);
\t\t\tConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
\t\t\tif (StringUtils.hasLength(typeAttr)) {
\t\t\t\tvalueHolder.setType(typeAttr);
\t\t\t}
\t\t\tif (StringUtils.hasLength(nameAttr)) {
\t\t\t\tvalueHolder.setName(nameAttr);
\t\t\t}
\t\t\tvalueHolder.setSource(extractSource(ele));
// 將 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
// 這裡和上面的 IndexedArgumentValue 類似,只不過上面是 Map,這裡是 List
\t\t\tbd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
\t\t}
\t\tfinally {
\t\t\tthis.parseState.pop();
\t\t}
\t}
}/<code>

上面代碼首先提取 constructor-arg 標籤中必要的屬性 ( index 、 type 、 name )。

  • 如果指定了 index 屬性:解析 constructor-arg 的子元素。使用 ConstructorArgumentsValues.ValueHolder 類型來封裝解析出來的元素。將 type 、 name 和 index 屬性一併封裝在 ConstructorArgumentsValues.ValueHolder 類型中,並添加到當前 BeanDefinition 的 ConstructorArgumentValues 中的 LinkedHashMap 類型的屬性 indexedArgumentValues 中。
  • 如果有指定 index 屬性:解析 constructor-arg 的子元素。使用 ConstructorArgumentsValues.ValueHolder 類型來封裝解析出來的元素。將 type 、 name 和 index 屬性一併封裝在 ConstructorArgumentsValues.ValueHolder 類型中,並添加到當前 BeanDefinition 的 ConstructorArgumentValues 中的 ArrayList 類型的屬性 genericArgumentValues 中。

parsePropertyValue

<code>public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { 

\tString elementName = (propertyName != null ?
\t\t\t"<property> element for property '" + propertyName + "'" :
\t\t\t"<constructor-arg> element");

\t// Should only have one child element: ref, value, list, etc.
// 獲取所有子節點,例如 list、map等
\tNodeList nl = ele.getChildNodes();
\tElement subElement = null;
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 跳過 description 或者 meta 不處理
\t\tif (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
\t\t\t\t!nodeNameEquals(node, META_ELEMENT)) {
\t\t\t// Child element is what we're looking for.
\t\t\tif (subElement != null) {
\t\t\t\terror(elementName + " must not contain more than one sub-element", ele);
\t\t\t}
\t\t\telse {
\t\t\t\tsubElement = (Element) node;
\t\t\t}
\t\t}
\t}
\t// 提取 ref 屬性
\tboolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 提取 value 屬性
\tboolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 如果同時有 ref 和 value 屬性 || 有 ref 或 value 屬性同時又有子元素,拋出異常
\tif ((hasRefAttribute && hasValueAttribute) ||
\t\t\t((hasRefAttribute || hasValueAttribute) && subElement != null)) {
\t\terror(elementName +
\t\t\t\t" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
\t}
\t// 只有 ref 屬性,使用 RuntimeBeanReference 封裝對應的 ref 名稱 (該 ref 值指向另一個 bean 的 beanName)
// RuntimeBeanReference 起到佔位符的作用, ref 指向的 beanName 將在運行時被解析成真正的 bean 實例引用
\tif (hasRefAttribute) {
\t\tString refName = ele.getAttribute(REF_ATTRIBUTE);
\t\tif (!StringUtils.hasText(refName)) {
\t\t\terror(elementName + " contains empty 'ref' attribute", ele);
\t\t}
\t\tRuntimeBeanReference ref = new RuntimeBeanReference(refName);

\t\tref.setSource(extractSource(ele));
\t\treturn ref;
\t}
// 只有 value 屬性,使用 TypedStringValue 封裝
\telse if (hasValueAttribute) {
\t\tTypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
\t\tvalueHolder.setSource(extractSource(ele));
\t\treturn valueHolder;
\t}
\telse if (subElement != null) {
// 解析子元素
\t\treturn parsePropertySubElement(subElement, bd);
\t}
\telse {
\t\t// Neither child element nor "ref" or "value" attribute found.
// 沒有子元素,也沒有 ref 和 value,直接拋出異常
\t\terror(elementName + " must specify a ref or value", ele);
\t\treturn null;
\t}
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
\treturn parsePropertySubElement(ele, bd, null);
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 校驗是否為默認的命名空間,如果不是則走解析自定義節點代碼
\tif (!isDefaultNamespace(ele)) {
\t\treturn parseNestedCustomElement(ele, bd);
\t}
// 解析 bean 節點
\telse if (nodeNameEquals(ele, BEAN_ELEMENT)) {
\t\tBeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
\t\tif (nestedBd != null) {
\t\t\tnestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
\t\t}
\t\treturn nestedBd;
\t}
// 解析 ref 節點
\telse if (nodeNameEquals(ele, REF_ELEMENT)) {
\t\t// A generic reference to any name of any bean.
\t\tString refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
\t\tboolean toParent = false;
\t\tif (!StringUtils.hasLength(refName)) {

\t\t\t// A reference to the id of another bean in a parent context.
\t\t\trefName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
\t\t\ttoParent = true;
\t\t\tif (!StringUtils.hasLength(refName)) {
\t\t\t\terror("'bean' or 'parent' is required for element", ele);
\t\t\t\treturn null;
\t\t\t}
\t\t}
\t\tif (!StringUtils.hasText(refName)) {
\t\t\terror(" element contains empty target attribute", ele);
\t\t\treturn null;
\t\t}
\t\tRuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
\t\tref.setSource(extractSource(ele));
\t\treturn ref;
\t}
// 解析 idref 節點
\telse if (nodeNameEquals(ele, IDREF_ELEMENT)) {
\t\treturn parseIdRefElement(ele);
\t}
// 解析 value 節點
\telse if (nodeNameEquals(ele, VALUE_ELEMENT)) {
\t\treturn parseValueElement(ele, defaultValueType);
\t}
// 解析 null 節點
\telse if (nodeNameEquals(ele, NULL_ELEMENT)) {
\t\t// It's a distinguished null value. Let's wrap it in a TypedStringValue
\t\t// object in order to preserve the source location.
\t\tTypedStringValue nullHolder = new TypedStringValue(null);
\t\tnullHolder.setSource(extractSource(ele));
\t\treturn nullHolder;
\t}
// 解析 array 節點
\telse if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
\t\treturn parseArrayElement(ele, bd);
\t}
// 解析 list 節點
\telse if (nodeNameEquals(ele, LIST_ELEMENT)) {
\t\treturn parseListElement(ele, bd);
\t}
// 解析 set 節點
\telse if (nodeNameEquals(ele, SET_ELEMENT)) {
\t\treturn parseSetElement(ele, bd);
\t}
// 解析 map 節點
\telse if (nodeNameEquals(ele, MAP_ELEMENT)) {

\t\treturn parseMapElement(ele, bd);
\t}
// 解析 props 節點
\telse if (nodeNameEquals(ele, PROPS_ELEMENT)) {
\t\treturn parsePropsElement(ele);
\t}
// 未知屬性,拋出異常
\telse {
\t\terror("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
\t\treturn null;
\t}
}
/<constructor-arg>/<property>/<code>

從上面的代碼來看,對構造函數中屬性元素的解析,步驟如下:

  1. 略過 description 或者 meta 。
  2. 提取 constructor-arg 上的 ref 和 value 屬性,以便於根據規則驗證正確性。其規則為在 constructor-arg 上不存在一下情況:同時存在 ref 和 value 屬性。存在 ref 或者 value 屬性,並且又有子元素。

parsePropertyElements

<code>public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 獲取所有子節點
\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
\t\tif (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
// 解析 property 節點
\t\t\tparsePropertyElement((Element) node, bd);
\t\t}
\t}
}

// 這裡是解析 property 標籤,<property>

public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 獲取 name 屬性
\tString propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// name 為空,拋出異常
\tif (!StringUtils.hasLength(propertyName)) {
\t\terror("Tag 'property' must have a 'name' attribute", ele);
\t\treturn;
\t}
\tthis.parseState.push(new PropertyEntry(propertyName));
\ttry {
\t// 出現兩個 name 相同的拋出異常
\t\tif (bd.getPropertyValues().contains(propertyName)) {
\t\t\terror("Multiple 'property' definitions for property '" + propertyName + "'", ele);
\t\t\treturn;
\t\t}
// 解析屬性值,跟構造器解析一樣,查看上方代碼
\t\tObject val = parsePropertyValue(ele, bd, propertyName);
// 用 name 和 val 封裝成 PropertyValue
\t\tPropertyValue pv = new PropertyValue(propertyName, val);
// 解析元數據,跟 beans 標籤內的 meta 一樣
\t\tparseMetaElements(ele, pv);
\t\tpv.setSource(extractSource(ele));
// 添加到 BeanDefiniton 的 PropertyValues 屬性中
\t\tbd.getPropertyValues().addPropertyValue(pv);
\t}
\tfinally {
\t\tthis.parseState.pop();
\t}
}/<code>

上面方法主要是遍歷 property 節點,然後解析屬性值封裝成 PropertyValue 添加到 BeanDefinition 的 PropertyValues 中。

注意: property 節點類似於 POJO 中的 set 方法, bean 中的屬性必需有 set 方法否則會拋出異常。

parseQualifierElements

<code>public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
// 獲取子節點

\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
\t\tif (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
// 解析 qualifier 節點
\t\t\tparseQualifierElement((Element) node, bd);
\t\t}
\t}
}

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 提取 type
\tString typeName = ele.getAttribute(TYPE_ATTRIBUTE);
// type 為空拋出異常
\tif (!StringUtils.hasLength(typeName)) {
\t\terror("Tag 'qualifier' must have a 'type' attribute", ele);
\t\treturn;
\t}
\tthis.parseState.push(new QualifierEntry(typeName));
\ttry {
\t\tAutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
\t\tqualifier.setSource(extractSource(ele));
// 提取 value
\t\tString value = ele.getAttribute(VALUE_ATTRIBUTE);
// value 不為空,設置到 AutowireCandidateQualifier 的 attribute 中
\t\tif (StringUtils.hasLength(value)) {
\t\t\tqualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
\t\t}
// 獲取子節點
\t\tNodeList nl = ele.getChildNodes();
\t\tfor (int i = 0; i < nl.getLength(); i++) {
\t\t\tNode node = nl.item(i);
// 如果是有 attribute 節點,進行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
\t\t\tif (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
\t\t\t\tElement attributeEle = (Element) node;
\t\t\t\tString attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
\t\t\t\tString attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
\t\t\t\tif (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
\t\t\t\t\tBeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
\t\t\t\t\tattribute.setSource(extractSource(attributeEle));
\t\t\t\t\tqualifier.addMetadataAttribute(attribute);
\t\t\t\t}
\t\t\t\telse {
\t\t\t\t\terror("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
\t\t\t\t\treturn;
\t\t\t\t}

\t\t\t}
\t\t}
// 設置 BeanDefinition 的 qualifier
\t\tbd.addQualifier(qualifier);
\t}
\tfinally {
\t\tthis.parseState.pop();
\t}
}/<code>

對於 qualifier 元素的獲取,我們大多數接觸的更多是註解的形式,在使用 Spring 框架中進行自動注入時,Spring 容器中匹配的候選 Bean 必需有且只有一個。如果存在多個類型相同的 Bean ,且按照類型注入時,Spirng 允許通過 Qualifier 指定注入 Bean 的名稱,這樣歧義就消除了。

7-1-2.BeanDefinitionReaderUtils#registerBeanDefinition

<code>public static void registerBeanDefinition(
\t\t\tBeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
\t\t\tthrows BeanDefinitionStoreException {

\t// Register bean definition under primary name.
// 獲取 beanName
\tString beanName = definitionHolder.getBeanName();
// 以 key-value 的形式註冊,key 為 beanName,value 為 BeanDefinition。見下文詳解
\tregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

\t// Register aliases for bean name, if any.
// 註冊 bean 的別名
\tString[] aliases = definitionHolder.getAliases();
\tif (aliases != null) {
\t\tfor (String alias : aliases) {
// 以 key-value 的形式註冊 bean 的別名,key 為別名,value 為 beanName。見下文詳解
\t\t\tregistry.registerAlias(beanName, alias);
\t\t}
\t}
}

// DefaultListableBeanFactory.java

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
\t\t\tthrows BeanDefinitionStoreException {

\tAssert.hasText(beanName, "Bean name must not be empty");
\tAssert.notNull(beanDefinition, "BeanDefinition must not be null");

\tif (beanDefinition instanceof AbstractBeanDefinition) {
\t\ttry {
\t\t\t// 驗證 Bean 的格式是否正確
\t\t\t((AbstractBeanDefinition) beanDefinition).validate();
\t\t}
\t\tcatch (BeanDefinitionValidationException ex) {
\t\t\tthrow new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
\t\t\t\t\t"Validation of bean definition failed", ex);
\t\t}
\t}

\tBeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
\t// 這裡判斷 BeanDefinition 是否存在
\tif (existingDefinition != null) {
\t\t// 這裡就是如果 Bean 定義以及存在,判斷是否可以覆蓋,默認是可以的
\t\t// Spring Boot 2.1開始這裡會手動設置 allowBeanDefinitionOverriding 的值為 false
\t\tif (!isAllowBeanDefinitionOverriding()) {
\t\t\tthrow new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
\t\t}
\t\telse if (existingDefinition.getRole() < beanDefinition.getRole()) {
\t\t\t// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
\t\t\tif (logger.isInfoEnabled()) {
\t\t\t\tlogger.info("Overriding user-defined bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with a framework-generated bean definition: replacing [" +
\t\t\t\t\t\texistingDefinition + "] with [" + beanDefinition + "]");
\t\t\t}
\t\t}
\t\telse if (!beanDefinition.equals(existingDefinition)) {
\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\tlogger.debug("Overriding bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with a different definition: replacing [" + existingDefinition +
\t\t\t\t\t\t"] with [" + beanDefinition + "]");
\t\t\t}
\t\t}
\t\telse {
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Overriding bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with an equivalent definition: replacing [" + existingDefinition +
\t\t\t\t\t\t"] with [" + beanDefinition + "]");

\t\t\t}
\t\t}
// 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中
\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t}
\telse {
\t\t// bean 是否已經開始創建
\t\tif (hasBeanCreationStarted()) {
\t\t\t// Cannot modify startup-time collection elements anymore (for stable iteration)
\t\t\tsynchronized (this.beanDefinitionMap) {
// 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中
\t\t\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t\t\t\t// 這裡將 beanDefinitionNames 寫時複製一份,類似於 CopyOnWriteArrayList
\t\t\t\tList<string> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
\t\t\t\tupdatedDefinitions.addAll(this.beanDefinitionNames);
\t\t\t\tupdatedDefinitions.add(beanName);
\t\t\t\tthis.beanDefinitionNames = updatedDefinitions;
\t\t\t\t// 從單例 Bean 註冊名稱列表中刪除當前 beanName
\t\t\t\tremoveManualSingletonName(beanName);
\t\t\t}
\t\t}
// bean 不在創建狀態中
\t\telse {
\t\t\t// Still in startup registration phase
\t\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t\t\t// 因為 ConcurrentHashMap 是無序的,這裡將 beanName 放入 ArrayList,記錄註冊順序
\t\t\tthis.beanDefinitionNames.add(beanName);
\t\t\t// 從單例 Bean 註冊名稱列表中刪除當前 beanName
\t\t\tremoveManualSingletonName(beanName);
\t\t}
\t\tthis.frozenBeanDefinitionNames = null;
\t}
\t// 如果存在相同的 beanName 的 BeanDefinition,或者 beanName 已經存在單例對象,則將該 beanName 對應的緩存信息、單例對象清除,因為這些對象都是由老的 BeanDefinition 創建的,需要被覆蓋掉。再用新的 BeanDefinition 來創建這些緩存和單例對象

\tif (existingDefinition != null || containsSingleton(beanName)) {
\t\tresetBeanDefinition(beanName);
\t}
}

// SimpleAliasRegistry.java
public void registerAlias(String name, String alias) {
\tAssert.hasText(name, "'name' must not be empty");
\tAssert.hasText(alias, "'alias' must not be empty");
\tsynchronized (this.aliasMap) {
// 如果別名和 beanName 相同,從緩存中移除
\t\tif (alias.equals(name)) {
\t\t\tthis.aliasMap.remove(alias);
\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\tlogger.debug("Alias definition '" + alias + "' ignored since it points to same name");
\t\t\t}
\t\t}
\t\telse {
\t\t\tString registeredName = this.aliasMap.get(alias);
// 如果別名以及註冊過,直接返回
\t\t\tif (registeredName != null) {
\t\t\t\tif (registeredName.equals(name)) {
\t\t\t\t\t// An existing alias - no need to re-register
\t\t\t\t\treturn;
\t\t\t\t}
// 如果不允許覆蓋,拋出異常
\t\t\t\tif (!allowAliasOverriding()) {
\t\t\t\t\tthrow new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
\t\t\t\t\t\t\tname + "': It is already registered for name '" + registeredName + "'.");
\t\t\t\t}
\t\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\t\tlogger.debug("Overriding alias '" + alias + "' definition for registered name '" +
\t\t\t\t\t\t\tregisteredName + "' with new target name '" + name + "'");
\t\t\t\t}
\t\t\t}
// 檢查 name 和 alias 是否存在循環引用。例如 A 的別名為 B,B的別名為A
\t\t\tcheckForAliasCircle(name, alias);
// 將 alias 和 name 以 key-value 對放入到 aliasMap 中,進行緩存
\t\t\tthis.aliasMap.put(alias, name);
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
\t\t\t}
\t\t}

\t}
}/<string>/<code>

上面代碼有兩個變量比較重要 beanDefinitionMap 和 beanDefinitionNames ,下面代碼是這兩個屬性在 DefaultListableBeanFactory 中的定義:

<code>/** Map of bean definition objects, keyed by bean name. */
// 緩存 BeanDefinition 的 Map,key 為 beanName,value 為 BeanDefinition
private final Map<string> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存 BeanDefinition 的註冊順序,保存的是 beanName
/** List of bean definition names, in registration order. */
private volatile List<string> beanDefinitionNames = new ArrayList<>(256);/<string>/<string>/<code>
  1. 如果 BeanDefinition 是 AbstractBeanDefinition 類型,驗證 Bean 的格式是否正確。這次效驗主要是對於 AbstractBeanDefinition 屬性中的 methodOverrides 的校驗,校驗 methodOverrides 是否與 工廠方法 並存或者 methodOverrides 中的方法根本不存在。
  2. 判斷該 beanName 的 BeanDefinition 是否已經註冊過;如果存在判斷是否允許覆蓋,允許的話直接替換,不允許直接拋出異常。默認的情況下是允許的,但是在 Spring Boot 2.1 開始這裡會手動的設置為不允許。
  3. beanName 對應的 BeanDefinition 以前沒有註冊過,判斷 bean 是否已經開始創建;如果在創建中對 beanDefinitionMap 進行加鎖 (這裡雖然 beanDefinitionMap 是線程安全的 ConcurrentHashMap ,單個操作是線程安全的但多個操作不是,所以這裡手動加鎖),然後將 beanName 和 BeanDefinition 以 key-value 形式放入 beanDefinitionMap 緩存中,然後寫時複製一份 beanDefinitionNames ,將 beaName 緩存進去,記錄 bean 的註冊順序;如果不在創建直接將 BeanDefinition 和 beanName 分別放入 beanDefinitionMap 和 beanDefinitionNames 中。
  4. 最後判斷如果 BeanDefinition 已經註冊過,或者 beanName 已經存在單例對象,則將該 beanName 對應的緩存信息、單例對象清除,因為這些對象都是由老的 BeanDefinition 創建的,需要被覆蓋掉。再用新的 BeanDefinition 來創建這些緩存和單例對象。

總結

本文主要介紹了通過 XML 文件的方式註冊 Bean ,我們可以重新梳理一下思路:

  1. 解析 XML 文件,構建成 AbstractBeanDefinition (GenericBeanDefinition) 對象來存放所有解析出來的屬性。
  2. 將 AbstractBeanDefinition 、 beanName 、 aliasesArray 構建成 BeanDefinitionHolder 對象。
  3. 最後通過 BeanDefinitionHolder 將 beanName 和 BeanDefinition 註冊到 DefaultListableBeanFactory 中,也就是保存起來。上文提到的兩個比較重要的屬性 beanDefinitionNames 和 beanDefinitionMap ,在後面都會多次用到,可以重點關注一下。


原文 http://www.cnblogs.com/yijinqincai/p/12701046.html


分享到:


相關文章: