歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之解析 bean 標簽:開啟解析行程

點選上方“Java技術驛站”,選擇“置頂公眾號”。

有內涵、有價值的文章第一時間送達!

精品專欄

 

import 標簽解析完畢了,再看 Spring 中最複雜也是最重要的標簽 bean 標簽的解析過程。

在方法 parseDefaultElement() 中,如果遇到標簽 為 bean 則呼叫 processBeanDefinition() 方法進行 bean 標簽解析,如下:

  1.    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

  2.        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

  3.        if (bdHolder != null) {

  4.            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

  5.            try {

  6.                // Register the final decorated instance.

  7.                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

  8.            }

  9.            catch (BeanDefinitionStoreException ex) {

  10.                getReaderContext().error("Failed to register bean definition with name '" +

  11.                        bdHolder.getBeanName() + "'", ele, ex);

  12.            }

  13.            // Send registration event.

  14.            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

  15.        }

  16.    }

整個過程分為四個步驟

  1. 呼叫 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 進行元素解析,解析過程中如果失敗,傳回 null,錯誤由 ProblemReporter 處理。如果解析成功則傳回 BeanDefinitionHolder 實體 bdHolder。BeanDefinitionHolder 為持有 name 和 alias 的 BeanDefinition。

  2. 若實體 bdHolder 不為空,則呼叫 BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired() 進行自定義標簽處理

  3. 解析完成後,則呼叫 BeanDefinitionReaderUtils.registerBeanDefinition() 對 bdHolder 進行註冊

  4. 發出響應事件,通知相關的監聽器,完成 Bean 標簽解析

先看方法 parseBeanDefinitionElement(),如下:

  1.   public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {

  2.        // 解析 ID 屬性

  3.        String id = ele.getAttribute(ID_ATTRIBUTE);

  4.        // 解析 name 屬性

  5.        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

  6.        // 分割 name 屬性

  7.        List<String> aliases = new ArrayList<>();

  8.        if (StringUtils.hasLength(nameAttr)) {

  9.            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);

  10.            aliases.addAll(Arrays.asList(nameArr));

  11.        }

  12.        String beanName = id;

  13.        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {

  14.            beanName = aliases.remove(0);

  15.            if (logger.isDebugEnabled()) {

  16.                logger.debug("No XML 'id' specified - using '" + beanName +

  17.                        "' as bean name and " + aliases + " as aliases");

  18.            }

  19.        }

  20.        // 檢查 name 的唯一性

  21.        if (containingBean == null) {

  22.            checkNameUniqueness(beanName, aliases, ele);

  23.        }

  24.        // 解析 屬性,構造 AbstractBeanDefinition

  25.        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

  26.        if (beanDefinition != null) {

  27.            // 如果 beanName 不存在,則根據條件構造一個 beanName

  28.            if (!StringUtils.hasText(beanName)) {

  29.                try {

  30.                    if (containingBean != null) {

  31.                        beanName = BeanDefinitionReaderUtils.generateBeanName(

  32.                                beanDefinition, this.readerContext.getRegistry(), true);

  33.                    }

  34.                    else {

  35.                        beanName = this.readerContext.generateBeanName(beanDefinition);

  36.                        String beanClassName = beanDefinition.getBeanClassName();

  37.                        if (beanClassName != null &&

  38.                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&

  39.                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

  40.                            aliases.add(beanClassName);

  41.                        }

  42.                    }

  43.                    if (logger.isDebugEnabled()) {

  44.                        logger.debug("Neither XML 'id' nor 'name' specified - " +

  45.                                "using generated bean name [" + beanName + "]");

  46.                    }

  47.                }

  48.                catch (Exception ex) {

  49.                    error(ex.getMessage(), ele);

  50.                    return null;

  51.                }

  52.            }

  53.            String[] aliasesArray = StringUtils.toStringArray(aliases);

  54.            // 封裝 BeanDefinitionHolder

  55.            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);

  56.        }

  57.        return null;

  58.    }

這個方法還沒有對 Bean 標簽進行解析,只是在解析動作之前做了一些功能架構,主要的工作有:

  • 解析 id、name 屬性,確定 alias 集合,檢測 beanName 是否唯一

  • 呼叫方法 parseBeanDefinitionElement() 對屬性進行解析並封裝成 GenericBeanDefinition 實體 beanDefinition

  • 根據所獲取的資訊(beanName、aliases、beanDefinition)構造 BeanDefinitionHolder 實體物件並傳回。

這裡有必要說下 beanName 的命名規則:如果 id 不為空,則 beanName = id;如果 id 為空,但是 alias 不空,則 beanName 為 alias 的第一個元素,如果兩者都為空,則根據預設規則來設定 beanName。

上面三個步驟第二個步驟為核心方法,它主要承擔解析 Bean 標簽中所有的屬性值。如下:

  1.   public AbstractBeanDefinition parseBeanDefinitionElement(

  2.            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

  3.        this.parseState.push(new BeanEntry(beanName));

  4.        String className = null;

  5.        // 解析 class 屬性

  6.        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {

  7.            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();

  8.        }

  9.        String parent = null;

  10.        // 解析 parent 屬性

  11.        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {

  12.            parent = ele.getAttribute(PARENT_ATTRIBUTE);

  13.        }

  14.        try {

  15.            // 建立用於承載屬性的 GenericBeanDefinition 實體

  16.            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

  17.            // 解析預設 bean 的各種屬性

  18.            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

  19.            // 提取 description

  20.            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

  21.            // 解析元資料

  22.            parseMetaElements(ele, bd);

  23.            // 解析 lookup-method 屬性

  24.            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

  25.            // 解析 replaced-method 屬性

  26.            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

  27.            // 解析建構式引數

  28.            parseConstructorArgElements(ele, bd);

  29.            // 解析 property 子元素

  30.            parsePropertyElements(ele, bd);

  31.            // 解析 qualifier 子元素

  32.            parseQualifierElements(ele, bd);

  33.            bd.setResource(this.readerContext.getResource());

  34.            bd.setSource(extractSource(ele));

  35.            return bd;

  36.        }

  37.        catch (ClassNotFoundException ex) {

  38.            error("Bean class [" + className + "] not found", ele, ex);

  39.        }

  40.        catch (NoClassDefFoundError err) {

  41.            error("Class that bean class [" + className + "] depends on not found", ele, err);

  42.        }

  43.        catch (Throwable ex) {

  44.            error("Unexpected failure during bean definition parsing", ele, ex);

  45.        }

  46.        finally {

  47.            this.parseState.pop();

  48.        }

  49.        return null;

  50.    }

到這裡,Bean 標簽的所有屬性我們都可以看到其解析的過程,也就說到這裡我們已經解析一個基本可用的 BeanDefinition。

由於解析過程較為漫長,篇幅較大,為了更好的觀看體驗,將這篇博文進行拆分。下篇部落格主要介紹 BeanDefinition,以及解析預設 Bean 的過程( parseBeanDefinitionAttributes()

【死磕 Spring】----- IOC 之 獲取 Document 物件

【死磕 Spring】----- IOC 之 註冊 BeanDefinition

【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 Spring 統一資源載入策略

【死磕 Spring】----- IOC 之深入理解 Spring IoC

END

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖