1. 程式人生 > 其它 >Spring 原始碼閱讀-1-xml載入解析.md

Spring 原始碼閱讀-1-xml載入解析.md

Spring xml解析,BeanDefinition 註冊過程中的原始碼

XML載入解析

1、 載入 Bean 定義

1.1 解析XML 處理Document

XmlBeanDefinitionReader#loadBeanDefinitions

/**
 * 載入xml資源,解析xml所有標籤,將xml對應的bean封裝為BeanDefinition(真實型別為GenericBeanDefinition),並註冊
 * 返回載入的BeanDefinition數量
 */
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   // EncodedResource 增加了encoding以及charset屬性
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
    // 將當前載入的xml記錄一下。正在載入
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
      // 獲取配置資源輸入檔案流物件(xml檔案輸入流)
      InputStream inputStream = encodedResource.getResource().getInputStream();
      // org.xml.sax.InputSource
      // 封裝為 InputSource 為 xml解析做準備
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
          inputSource.setEncoding(encodedResource.getEncoding());
      }
      // 真正的處理
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	....
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
          // 載入結束刪除
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   
}

此方法為載入xml的最上層方法,首先接在xml配置檔案,然後將檔案封裝為InputSource,然後將真正對xml的處理交給doLoadBeanDefinitions方法處理。

XmlBeanDefinitionReader#doLoadBeanDefinitions


/**
 * 將任務進一步拆分
 * 將xml檔案解析封裝為Document
 * 然後根據Document註冊bean資訊
 * @param inputSource
 * @param resource
 * @return
 * @throws BeanDefinitionStoreException
 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // 獲取對 XML 檔案的驗證模式並載入xml檔案為document
      Document doc = doLoadDocument(inputSource, resource);
      // 根據返回的 Document 註冊 Bean 資訊
      return registerBeanDefinitions(doc, resource);
   }
  ...
}

此方法進一步拆分任務,DefaultDocumentLoader#loadDocument 將xml流解析成Document物件,XmlBeanDefinitionReader#registerBeanDefinitions根據Document物件進一步解析,註冊bean資訊。

XmlBeanDefinitionReader#doLoadDocument

/**
 * 解析XML 
 * 允許使用標準 XML 實體將 XML 片段包含到應用程式上下文定義中,例如將大型 XML 檔案拆分為各種模組
 */
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
   // getValidationModeForResource  獲取對 XML 檔案的驗證模式   (1、dtd 2、xsd) 如果未設定將載入流自動判斷模式
   // getEntityResolver XML 解析器
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}

XmlBeanDefinitionReader#registerBeanDefinitions中通過DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions在給定的根 元素中註冊每個 bean 定義。

protected void doRegisterBeanDefinitions(Element root) {
   ....
   if (this.delegate.isDefaultNamespace(root)) {
      // 處理 beans 的 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)) {
            if (logger.isInfoEnabled()) {
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }
   // 前置處理留給子類處理
   preProcessXml(root);
   // 真正的解析bean定義
   parseBeanDefinitions(root, this.delegate);
   // 後置處理留給子類處理
   postProcessXml(root);
   ....
}

DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   // 對beans 處理
   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) {
            // 對 bean處理
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               // 處理 預設的 http //w ww.springframework org/scherna beans
               parseDefaultElement(ele, delegate);
            }
            else {
                // 處理自定義的 比如 tx下的
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

到此,開始根據root(beans)節點迴圈解析子標籤,子標籤又分為預設標籤以及自定義標籤,這兩種處理方式截然不同。一下都是在說預設標籤,自定義標籤單獨說明。

到此已完成的任務

  1. 載入xml resource資源,並且封裝為EncodedResource(可以設定charset)。
  2. 將xml資源輸入流物件封裝為InputSource,供DocumentLoader載入並解析為Document物件(需要指定Xml解析器,以及XMl格式為標準模式的哪一種(xsd|dtd))。
  3. 通過Document獲取root節點開始解析,在此之前可以通過前置處理器處理(root),在此之後可以通過後置處理器處理(root)。
  4. 迴圈解析root下的所有子標籤,標籤分為預設標籤以及自定義標籤,處理截然不同。預設標籤,例如 bean、import等,自定義標籤,例如tx等。

1.2 解析預設標籤

DefaultBeanDefinitionDocumentReader#parseDefaultElement

/**
 * beans.xsd 預設四種類型   import bean beans alias
 * @param ele
 * @param delegate
 */
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // import 型別處理
   // 是加上就是遞迴呼叫 載入配置檔案定義bean方法
   // XMLBeanDefinitionReader.loadBeanDefinitions(resource)
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // alias 處理
   // 跟bean中name屬性處理相同
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // bean 處理
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // beans 處理
   // 巢狀一不簽到沒啥大的區別  迴圈呼叫  beans 解析過程
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

此方法對各個預設標籤進行不同處理,import與beans實則都是遞迴呼叫解析過程,所以主要看alias與bean。

bean標籤解析

DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 將bean 下的各種 預設屬性 等 進行解析 並存放到bdholder物件中
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // bdholder 不為空的情況下檢視 是否還有 自定義 屬性 並解析
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      // 解析完成後 bdHolder 中會有這個bean標籤的全部資訊
      try {
         // 然後在委託 BeanDefinitionReaderUtils.registerBeanDefinition 進行註冊
         // Register the final decorated instance.
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // 通知此bean已經註冊完成
      // 為了以後擴充套件的目前沒有實現
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

此方法中BeanDefinitionParserDelegate.parseBeanDefinitionElement對預設標籤進行解析,並將解析後的資訊儲存到BeanDefinitionHolder

BeanDefinitionParserDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   // 解析id
   String id = ele.getAttribute(ID_ATTRIBUTE);
   // 解析 name
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   // 分割 name
   List<String> aliases = new ArrayList<>();
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }
   String beanName = id;
   // id 為null 使用 name 的第一個
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
   	  ...
   }
   // 檢查beanName的在bean容器中的唯一性
   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }
   // 進一步解析bean的其他所有屬性  就是一個bean中的所有屬性和元素解析成javaBean,即 AbstractBeanDefinition
   // xml中的bean有什麼  AbstractBeanDefinition 類 就有什麼
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
           ...
            // 不存在beanName 根絕Spring  提供的命名規則為當前bean生成 beanname
            if (containingBean != null) {
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               beanName = this.readerContext.generateBeanName(beanDefinition);
               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  aliases.add(beanClassName);
               }
            }
           ...
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }
   return null;
}

此方法中對bean標籤的屬性(只是解析id以及name,其他的也是在下面的方法中解析)進行解析,對子標籤的解析交給下層進行處理(BeanDefinitionParserDelegate#parseBeanDefinitionElement),對bean下的自定義標籤的解析單獨處理。

BeanDefinitionParserDelegate#parseBeanDefinitionElement

/**
 * Parse the bean definition itself, without regard to name or aliases. May return
 * {@code null} if problems occurred during the parsing of the bean definition.
 */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {
   // 一個集合(LinkedList ) 將正在解析的beanname記錄
   this.parseState.push(new BeanEntry(beanName));
   // 解析 class
   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   // 解析 parent
   String parent = null;
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }
	...
      // 建立用於承載屬性AbstractBeanDefinition型別的GenericBeanDefinition
      // BeanDefinition 是配置檔案<bean>元素標籤在容器中的內部表示形式
      // <bean>元素標籤擁有
      //class scope lazy-init 等配置屬性, BeanDefinition 則提供了相應的 beanClass scope lazyInit
      //屬性, BeanDefinition <bean>中的屬性是 一對應的
      // Spring 通過 BeanDefinition 將配置檔案中的<bea >配置資訊轉換為容器的內部表示,並將
      //這些 BeanDefinition 註冊到 BeanDefinitionReistry中。 Spring 容器的 BeanDefinitionRegistrγ 就像
      //Spring 配置資訊的記憶體資料庫,主要是以 map 的形式儲存,後續操作直接從 BeanDefinition Registry 中讀取配置資訊
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
      // 硬編碼解析預設 bean的各種預設屬性
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      // 一下全部是子元素的 解析
      // 提取 description
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
      // 解析 meta  元資料 子元素
      // 取出全部meta子元素, 將key-value 封裝成 BeanMetadataAttribute物件(還包括)儲存到 BeanMetadataAttributeAccessor中
      // AbstractBeanDefinition 集成了 BeanMetadataAttributeAccessor
      parseMetaElements(ele, bd);
      // 解析 lookup-method
      // 抽象方法 返回 指定的bean  <lookup-method name="getBean" bean="bean">  getBean 為抽象方法
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // 解析 replaced-method
      // 與 lookup-method 的區別是  lookup-method自定義 返回的bean  ,replaced-method可以將方法的邏輯進行替換
      // 都會 封裝成 MethodOverride  存放到  bd的methodOverrides中
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
      // 解析 建構函式引數
      parseConstructorArgElements(ele, bd);
      // 解析property子元素
      parsePropertyElements(ele, bd);
      // 解析 qualifier 子元素   作用指定beanName 消除歧義
      parseQualifierElements(ele, bd);
      bd.setResource(this.readerContext.getResource());
      bd.setSource(extractSource(ele));

      return bd;
	...
   return null;
}

此方法首先解析bean標籤的class以及parent,然後建立建立預設的AbstractBeanDefinition,然後通過BeanDefinitionParserDelegate#parseBeanDefinitionAttributes解析bean的各種預設屬性,然後在此方法中解析其他的子標籤(例如lookup-method、property、constructor等)。

BeanDefinitionParserDelegate#parseBeanDefinitionAttributes

/**
 * 將給定 bean 元素的屬性應用於給定 bean 定義
 * Apply the attributes of the given bean element to the given bean * definition.
 * @param ele bean declaration element
 * @param beanName bean name
 * @param containingBean containing bean definition
 * @return a bean definition initialized according to the bean element attributes
 */
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
      @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

   // 解析 singleton 使用singleton會 提示錯誤
   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   }
   // 解析 scope
   else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   }
   // 在嵌入 beanDifinition 情況下且沒有單獨指定 scope 屬性則使用父類預設的屬性
   else if (containingBean != null) {
      // Take default from containing bean in case of an inner bean definition.
      bd.setScope(containingBean.getScope());
   }
   // 解析 abstract
   if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
      bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
   }
   // 解析 lazy-init
   String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
   if (isDefaultValue(lazyInit)) {
      lazyInit = this.defaults.getLazyInit();
   }
   // 非true字串或空都將是false
   bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
   // 解析 autowire
   String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
   bd.setAutowireMode(getAutowireMode(autowire));
   // 解析 depends-on
   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }
   // 解析autowire-candidate屬性
   String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
   if (isDefaultValue(autowireCandidate)) {
      String candidatePattern = this.defaults.getAutowireCandidates();
      if (candidatePattern != null) {
         String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
         bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
      }
   }
   else {
      bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
   }
   // 解析 primary 屬性
   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
      bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }
   // 解析 init-method
   if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
      String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
      bd.setInitMethodName(initMethodName);
   }
   else if (this.defaults.getInitMethod() != null) {
      bd.setInitMethodName(this.defaults.getInitMethod());
      bd.setEnforceInitMethod(false);
   }
   // 解析 destroy-method
   if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
      String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
      bd.setDestroyMethodName(destroyMethodName);
   }
   else if (this.defaults.getDestroyMethod() != null) {
      bd.setDestroyMethodName(this.defaults.getDestroyMethod());
      bd.setEnforceDestroyMethod(false);
   }
   // 解析 factory-method
   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   // 解析factory-bean
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }
   return bd;
}

至此一個bean的預設標籤解析工作就完成了,xml中bean標籤的所有資訊都被封裝到了AbstractBeanDefinition物件中。

bean下的自定義標籤的解析將單獨說明

DefaultBeanDefinitionDocumentReader#processBeanDefinition中解析完 bean的預設標籤以及自定義標籤後,需要將儲存著bean定義資訊的物件(BeanDefinitionHolder),通過DefaultListableBeanFactory#registerBeanDefinition方法註冊到Spring容器中。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
    ...
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         //  註冊前的最後一次校驗,這裡的校驗不同於之前XML檔案校驗,
         //  主要是對 AbstractBeanDefinition 中 methodOverrides 校驗
         //  校驗 methodOverrides 是否與工廠方法並存或者 methodOverrides 對應的方法根本不存在
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
    ...
   }
   // 執行緒安全的map 所以這裡不需要在使用 synchronized
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   // 通過 beanName 查詢的 bean 已經存在
   if (existingDefinition != null) {
      // 一系列判斷
      // 是否允許Bean定義重寫 檢查是否允許覆蓋
      // 如果對應的 BeanName 已經註冊且在配置中配置了 bean 不允許被覆蓋,則丟擲異常
      // 當前要儲存的是否和取出來的是一個物件
      // 註冊beanDefinition
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
       // bean是否被標記為已建立
       // 需要全複製
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);
         // 記錄 bean 名稱
         this.beanDefinitionNames.add(beanName);
         this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }
	// bean被新的定義覆蓋了   或者是   單例
   if (existingDefinition != null || containsSingleton(beanName)) {
      // 重置給定 bean 的所有 bean 定義快取,包括從它派生的 bean 快取
      resetBeanDefinition(beanName);
   }
   else if (isConfigurationFrozen()) {
      clearByTypeCache();
   }
}

至此bean的解析,註冊全部完成

import 除去解析特定的幾個屬性外就是遞迴呼叫beans解析過程。

beans 下包含 beans的情況也是遞迴呼叫beans的解析過程。

aliase 主要是解析預設屬性,並記錄別名與beanname的關聯關係。

已完成的任務:

  1. root下的不同子標籤,分別進行解析(import bean beans alias)

  2. 解析bean標籤所有預設屬性(class、id等)

  3. 解析bean標籤所有子標籤(property等)

  4. 解析bean下所有自定義標籤

  5. 將承載xml中bean標籤全部資訊的AbstractBeanDefinition物件註冊到Spring 容器(其實就是map,儲存到裡面)

    注意 自定義標籤的解析未說明,單獨說明。

                                                                                                                       ---- **Cover 《Spring 原始碼深度解析》**