07.Spring Bean 解析 - BeanDefinitionDocumentReader

來源:互聯網
上載者:User

標籤:string   method   exce   ext.get   stp   override   建立   getbean   art   

基本概念

BeanDefinitionDocumentReader ,該類的作用有兩個,完成 BeanDefinition 的解析和註冊 。

  • 解析:其實是解析 Ddocument 的內容並將其添加到 BeanDefinition 執行個體的過程。

  • 註冊:就是將 BeanDefinition 添加進 BeanDefinitionHolder 的過程,這樣做的目的是儲存它的資訊。

下面來看它的介面定義,該介面只定義了一個方法負責完成解析和註冊的工作:

public interface BeanDefinitionDocumentReader {    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)throws BeanDefinitionStoreException;}

再來看它的繼承關係,預設只有一個實作類別:

源碼分析

接下來來看 DefaultBeanDefinitionDocumentReader 類中的 registerBeanDefinitions 方法。

首先來看該方法的入參:

  • Document:代指 Spring 的設定檔資訊,通過 BeanDefinitionReader 解析 Resrouce 執行個體得到。

  • XmlReaderContext :主要包含了 BeanDefinitionReader 和 Resrouce 。

再來看它的具體流程:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {    this.readerContext = readerContext;    // 日誌輸出... // 取得根項目,即 XML 檔案中的 <beans> 標籤 Element root = doc.getDocumentElement(); // 關鍵 -> 繼續 Document 的解析 doRegisterBeanDefinitions(root);}

關於 doRegisterBeanDefinitions,該方法的主要作用有:

  • 建立 BeanDefinitionParserDelegate 對象,用於將 Document 的內容轉成 BeanDefinition 執行個體,也就是上面提到的解析過程,BeanDefinitionDocumentReader 本身不具備該功能而是交給了該類來完成。

  • 取得 beans 標籤中 profile 的屬性內容,該標籤主要用於環境的切換。例如開發過程中,一般存在測試環境和正式環境,兩者之間可能存在不同的資料來源。若想要實現環境的快速切換,就可以利用 profile 來配置。具體實現這裡暫不探究。

protected void doRegisterBeanDefinitions(Element root) {    // 建立 delegate 對象    BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); // 驗證 XML 檔案的命名空間,即判斷是否含有 xmlns="http://www.springframework.org/schema/beans" if (this.delegate.isDefaultNamespace(root)) { // 解析 profile String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } // 空方法 preProcessXml(root); // 關鍵 -> 開始解析 Bean 定義 parseBeanDefinitions(root, this.delegate); // 空方法 postProcessXml(root); this.delegate = parent;}

下面開始通過遍曆取得 beans 元素的所有子節點。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {    // 驗證 XML 檔案的命名空間    if (delegate.isDefaultNamespace(root)) { // 取得 <beans> 的所有子節點 NodeList nl = root.getChildNodes(); // 遍曆 <beans> 的子節點 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); // 判斷節點是不是 Element 類型 if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { // 關鍵 -> 解析 <beans> 子節點的內容 parseDefaultElement(ele, delegate); }else { // 解析自訂元素,暫不探究 delegate.parseCustomElement(ele); } } } }else { delegate.parseCustomElement(root); }}

在拿到了子節點後,開始解析 beans 標籤的子節點,常見的標籤有 import,alias,bean,beans 等。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {    // 處理 <import> 標籤,將資源進行合并再統一解析    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // 處理 <alias> 標籤 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } // 關鍵-> 處理 <bean> 標籤 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } // 處理 <beans> 標籤,回到開始解析 document 的地方 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); }}

這裡關鍵來探究下 bean 的解析過程:

  • 上面提到 bean 標籤的具體解析工作交給 BeanDefinitionParserDelegate 類來完成。

  • 在完成解析取得 BeanDefinition(被添加進了 BeanDefinitionHolder ) 對象之後利用 BeanDefinitionRegistry 完成註冊過程。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {    // 關鍵 -> ①交給委託類 delegate 來完成解析過程 ,並返回 BeanDefinitionHolder 對象    // 該Object Storage Service了 BeanDefinition 的基本資料 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); // 交給委託類 delegate 來完成修飾過程,這裡暫不探究 if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 關鍵 -> ②註冊最後的裝飾執行個體 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); }catch (BeanDefinitionStoreException ex) { //錯誤輸出... } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}

觀察上述代碼,發現 DefaultBeanDefinitionDocumentReader 的主要職責是解析 Document ,取得設定檔(這裡指 xml )中定義的標籤內容;而解析標籤的過程交給 BeanDefinitionParserDelegate 類完成;註冊過程交給了 BeanDefinitionRegistry 介面來完成。

1.BeanDefinition 解析

在 DefaultBeanDefinitionDocumentReader 關於 bean 標籤的解析方法如下:

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  • 1

下面來看下 BeanDefinitionParserDelegate 的 parseBeanDefinitionElement 方法。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {    return parseBeanDefinitionElement(ele, null);}public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { // 取得 <bean> 標籤的 id 屬性 String id = ele.getAttribute(ID_ATTRIBUTE); // 取得 <bean> 標籤的 name 屬性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 建立 List 用於存方法 alsas(別名)集合 List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { // 將 nameArr 按照(,)或(;)分割成數組 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); // 添加進行別名集合 aliases.addAll(Arrays.asList(nameArr)); } // 判斷 id 是否為空白,若為空白則取別名集合的第一個元素當作 id ,並將其從別名集合當中移除 String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); // 日誌輸出... } // 檢查 id(標識)和 alias(名別)是否唯一 if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 關鍵 -> 解析 <bean> 標籤的 class 屬性 以及相關特性標籤,並將其添加進 beanDefinition 返回 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { // 判斷是否存在 beanName(id) if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); }else { // 由 Spring 自動產生 beanName(id) beanName = this.readerContext.generateBeanName(beanDefinition); // 取得 bean 的 完整類名可用 String beanClassName = beanDefinition.getBeanClassName(); // 將 beanName 添加進 alais 集合 if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } // 日誌輸出... }catch (Exception ex) { error(ex.getMessage(), ele); return null; } } // 將 aliases 集合數組化 String[] aliasesArray = StringUtils.toStringArray(aliases); // 關鍵 -> 返回一個 BeanDefinitionHolder 執行個體,用於儲存資訊 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null;}

再來看解析 bean 標籤的 class 屬性以及相關特性標籤,並將其添加進 beanDefinition 返回的具體過程:

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {    // 入棧操作,往 parseState 中添加一個 建立的 BeanEntry    this.parseState.push(new BeanEntry(beanName)); // 取得 <bean> 標籤的 class 屬性 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { // 取得 <bean> 標籤的 parent 屬性 String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } // 根據 class,parent 的屬性值建立一個 BeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 取得 <bean> 標籤的其他特性屬性,並添加進 BeanDefinition 。如: // scope、 // lazy-init // autowire // primary、autowire-candidate // depends-on、dependency-check // init-method、destroy-method // factory-method、factory-bean parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 解析 <mate> 標籤 parseMetaElements(ele, bd); // 解析 <lookup-method> 標籤 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 解析 <replaced-method> 標籤 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析 <constructor-arg> 標籤 parseConstructorArgElements(ele, bd); // 解析 <property> 標籤 parsePropertyElements(ele, bd); // 解析 <qualifier> 標籤 parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; }catch (ClassNotFoundException ex) { // 錯誤輸出... }catch (NoClassDefFoundError err) { // 錯誤輸出... }catch (Throwable ex) { // 錯誤輸出... }finally { // 出棧操作 this.parseState.pop(); } return null;}
2.Beandefinition 註冊

在 DefaultBeanDefinitionDocumentReader 關於 bean 標籤的註冊方法如下:

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  • 1

下面來看 BeanDefinitionReaderUtils 的 registerBeanDefinition 方法。該方法的主要作用是調用註冊器完成註冊過程。

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)    throws BeanDefinitionStoreException { // 取得 BeanDefinition 執行個體的標識 String beanName = definitionHolder.getBeanName(); // 關鍵 -> 調用註冊器實現註冊過程 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 取得 BeanDefinition 執行個體的所有別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { // 往註冊器的 aliasMap 添加 alias 的過程 registry.registerAlias(beanName, alias); } }}

 

07.Spring Bean 解析 - BeanDefinitionDocumentReader

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.