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

【死磕 Spring】—– IOC 之解析 bean 標簽:constructor-arg、property 子元素

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

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

精品專欄

 

上篇部落格(【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)分析了 meta 、 lookup-method、replace-method 三個子元素,這篇部落格分析 constructor-arg 、property、qualifier 三個子元素。

constructor-arg 子元素

舉個小慄子:

  1. public class StudentService {

  2.    private String name;

  3.    private Integer age;

  4.    private BookService bookService;

  5.    StudentService(String name, Integer age, BookService bookService){

  6.        this.name = name;

  7.        this.age = age;

  8.        this.bookService = bookService;

  9.    }

  10. }

  11.    "bookService" class="org.springframework.core.service.BookService"/>

  12.    "studentService" class="org.springframework.core.service.StudentService">

  13.        "0" value="chenssy"/>

  14.        "age" value="100"/>

  15.        "bookService" ref="bookService"/>

  16.    

StudentService 定義一個建構式,配置檔案中使用 constructor-arg 元素對其配置,該元素可以實現對 StudentService 自動尋找對應的建構式,併在初始化的時候將值當做引數進行設定。 parseConstructorArgElements() 方法完成 constructor-arg 子元素的解析。

  1.    public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {

  2.        NodeList nl = beanEle.getChildNodes();

  3.        for (int i = 0; i < nl.getLength(); i++) {

  4.            Node node = nl.item(i);

  5.            if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {

  6.                parseConstructorArgElement((Element) node, bd);

  7.            }

  8.        }

  9.    }

遍歷所有子元素,如果為 constructor-arg 則呼叫 parseConstructorArgElement() 進行解析。

  1.    public void parseConstructorArgElement(Element ele, BeanDefinition bd) {

  2.        // 提取 index、type、name 屬性值

  3.        String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);

  4.        String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);

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

  6.        // 如果有index

  7.        if (StringUtils.hasLength(indexAttr)) {

  8.            try {

  9.                int index = Integer.parseInt(indexAttr);

  10.                if (index < 0) {

  11.                    error("'index' cannot be lower than 0", ele);

  12.                }

  13.                else {

  14.                    try {

  15.                        // 構造一個 ConstructorArgumentEntry 並將其加入到 ParseState 中

  16.                        this.parseState.push(new ConstructorArgumentEntry(index));

  17.                        // 解析 ele 對應屬性元素

  18.                        Object value = parsePropertyValue(ele, bd, null);

  19.                        // 根據解析的屬性元素構造一個 valueHolder 物件

  20.                        ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);

  21.                        if (StringUtils.hasLength(typeAttr)) {

  22.                            valueHolder.setType(typeAttr);

  23.                        }

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

  25.                            valueHolder.setName(nameAttr);

  26.                        }

  27.                        //

  28.                        valueHolder.setSource(extractSource(ele));

  29.                        // 不允許重覆指定相同引數

  30.                        if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {

  31.                            error("Ambiguous constructor-arg entries for index " + index, ele);

  32.                        }

  33.                        else {

  34.                            // 加入到 indexedArgumentValues 中國

  35.                            bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);

  36.                        }

  37.                    }

  38.                    finally {

  39.                        this.parseState.pop();

  40.                    }

  41.                }

  42.            }

  43.            catch (NumberFormatException ex) {

  44.                error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);

  45.            }

  46.        }

  47.        else {

  48.            try {

  49.                this.parseState.push(new ConstructorArgumentEntry());

  50.                Object value = parsePropertyValue(ele, bd, null);

  51.                ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);

  52.                if (StringUtils.hasLength(typeAttr)) {

  53.                    valueHolder.setType(typeAttr);

  54.                }

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

  56.                    valueHolder.setName(nameAttr);

  57.                }

  58.                valueHolder.setSource(extractSource(ele));

  59.                bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);

  60.            }

  61.            finally {

  62.                this.parseState.pop();

  63.            }

  64.        }

  65.    }

首先獲取 index、type、name 三個屬性值,然後根據是否存在 index 來區分。其實兩者邏輯都差不多,總共分為如下幾個步驟(以有 index 為例):

  1. 構造 ConstructorArgumentEntry 物件並將其加入到 ParseState 佇列中。ConstructorArgumentEntry 表示建構式的引數。

  2. 呼叫 parsePropertyValue() 解析 constructor-arg 子元素,傳回結果值

  3. 根據解析的結果值構造 ConstructorArgumentValues.ValueHolder 實體物件

  4. 將 type、name 封裝到 ConstructorArgumentValues.ValueHolder 中,然後將 ValueHolder 實體物件新增到 indexedArgumentValues 中。

無 index 的處理邏輯差不多,只有幾點不同:構造 ConstructorArgumentEntry 物件時是呼叫無參建構式;最後是將 ValueHolder 實體新增到 genericArgumentValues 中。

parsePropertyValue() 對子元素進一步解析。

  1.    public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {

  2.        String elementName = (propertyName != null) ?

  3.                " element for property '" + propertyName + "'" :

  4.                " element";

  5.        NodeList nl = ele.getChildNodes();

  6.        Element subElement = null;

  7.        for (int i = 0; i < nl.getLength(); i++) {

  8.            Node node = nl.item(i);

  9.            // meta 、description 不處理

  10.            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&

  11.                    !nodeNameEquals(node, META_ELEMENT)) {

  12.                // Child element is what we're looking for.

  13.                if (subElement != null) {

  14.                    error(elementName + " must not contain more than one sub-element", ele);

  15.                }

  16.                else {

  17.                    subElement = (Element) node;

  18.                }

  19.            }

  20.        }

  21.        // 解析 ref 元素

  22.        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);

  23.        // 解析 value 元素

  24.        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

  25.        // constructor-arg 子元素有兩種情況不存在

  26.        // 1. 即存在 ref 又存在 value

  27.        // 2. 存在 ref 或者 value,但是又有子元素

  28.        if ((hasRefAttribute && hasValueAttribute) ||

  29.                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {

  30.            error(elementName +

  31.                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);

  32.        }

  33.        if (hasRefAttribute) {

  34.            // 獲取 ref 屬性值

  35.            String refName = ele.getAttribute(REF_ATTRIBUTE);

  36.            if (!StringUtils.hasText(refName)) {

  37.                error(elementName + " contains empty 'ref' attribute", ele);

  38.            }

  39.            // 將 ref 屬性值構造為 RuntimeBeanReference 實體物件

  40.            RuntimeBeanReference ref = new RuntimeBeanReference(refName);

  41.            ref.setSource(extractSource(ele));

  42.            return ref;

  43.        }

  44.        else if (hasValueAttribute) {

  45.            // 解析 value 屬性值,構造 TypedStringValue 實體物件

  46.            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));

  47.            valueHolder.setSource(extractSource(ele));

  48.            return valueHolder;

  49.        }

  50.        else if (subElement != null) {

  51.            // 解析子元素

  52.            return parsePropertySubElement(subElement, bd);

  53.        }

  54.        else {

  55.            // Neither child element nor "ref" or "value" attribute found.

  56.            error(elementName + " must specify a ref or value", ele);

  57.            return null;

  58.        }

  59.    }

  1. 提取 constructor-arg 子元素的 ref 和 value 的屬性值,對其進行判斷,以下兩種情況是不允許存在的

  • ref 和 value 屬性同時存在

  • 存在 ref 或者 value 且又有子元素

  1. 若存在 ref 屬性,則獲取其值並將其封裝進 RuntimeBeanReference 實體物件中

  2. 若存在 value 屬性,則獲取其值並將其封裝進 TypedStringValue 實體物件中

  3. 如果子元素不為空,則呼叫 parsePropertySubElement() 進行子元素進一步處理

對於 constructor-arg 子元素的巢狀子元素,需要呼叫 parsePropertySubElement() 進一步處理。

  1.    public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {

  2.        return parsePropertySubElement(ele, bd, null);

  3.    }

  4.    public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {

  5.        if (!isDefaultNamespace(ele)) {

  6.            return parseNestedCustomElement(ele, bd);

  7.        }

  8.        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {

  9.            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);

  10.            if (nestedBd != null) {

  11.                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);

  12.            }

  13.            return nestedBd;

  14.        }

  15.        else if (nodeNameEquals(ele, REF_ELEMENT)) {

  16.            // A generic reference to any name of any bean.

  17.            String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);

  18.            boolean toParent = false;

  19.            if (!StringUtils.hasLength(refName)) {

  20.                // A reference to the id of another bean in a parent context.

  21.                refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);

  22.                toParent = true;

  23.                if (!StringUtils.hasLength(refName)) {

  24.                    error("'bean' or 'parent' is required for element", ele);

  25.                    return null;

  26.                }

  27.            }

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

  29.                error(" element contains empty target attribute", ele);

  30.                return null;

  31.            }

  32.            RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);

  33.            ref.setSource(extractSource(ele));

  34.            return ref;

  35.        }

  36.        else if (nodeNameEquals(ele, IDREF_ELEMENT)) {

  37.            return parseIdRefElement(ele);

  38.        }

  39.        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {

  40.            return parseValueElement(ele, defaultValueType);

  41.        }

  42.        else if (nodeNameEquals(ele, NULL_ELEMENT)) {

  43.            // It's a distinguished null value. Let's wrap it in a TypedStringValue

  44.            // object in order to preserve the source location.

  45.            TypedStringValue nullHolder = new TypedStringValue(null);

  46.            nullHolder.setSource(extractSource(ele));

  47.            return nullHolder;

  48.        }

  49.        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {

  50.            return parseArrayElement(ele, bd);

  51.        }

  52.        else if (nodeNameEquals(ele, LIST_ELEMENT)) {

  53.            return parseListElement(ele, bd);

  54.        }

  55.        else if (nodeNameEquals(ele, SET_ELEMENT)) {

  56.            return parseSetElement(ele, bd);

  57.        }

  58.        else if (nodeNameEquals(ele, MAP_ELEMENT)) {

  59.            return parseMapElement(ele, bd);

  60.        }

  61.        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {

  62.            return parsePropsElement(ele);

  63.        }

  64.        else {

  65.            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);

  66.            return null;

  67.        }

  68.    }

上面對各個子類進行分類處理,詳細情況如果各位有興趣可以移步原始碼進行深一步的探究。

property 子元素

我們一般使用如下方式來使用 property 子元素。

  1.     id="studentService" class="org.springframework.core.service.StudentService">

  2.         name="name" value="chenssy"/>

  3.         name="age" value="18"/>

  4.    

對於 property 子元素的解析,Spring 呼叫 parsePropertyElements()。如下:

  1.    public void parsePropertyElements(Element beanEle, BeanDefinition bd) {

  2.        NodeList nl = beanEle.getChildNodes();

  3.        for (int i = 0; i < nl.getLength(); i++) {

  4.            Node node = nl.item(i);

  5.            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {

  6.                parsePropertyElement((Element) node, bd);

  7.            }

  8.        }

  9.    }

和 constructor-arg 子元素差不多,同樣是提取所有的 property 的子元素,然後呼叫 parsePropertyElement() 進行分析。

  1.    public void parsePropertyElement(Element ele, BeanDefinition bd) {

  2.        // 獲取 name 屬性

  3.        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);

  4.        if (!StringUtils.hasLength(propertyName)) {

  5.            error("Tag 'property' must have a 'name' attribute", ele);

  6.            return;

  7.        }

  8.        this.parseState.push(new PropertyEntry(propertyName));

  9.        try {

  10.            // 如果存在相同的 name

  11.            if (bd.getPropertyValues().contains(propertyName)) {

  12.                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);

  13.                return;

  14.            }

  15.            // 解析屬性值

  16.            Object val = parsePropertyValue(ele, bd, propertyName);

  17.            // 根據解析的屬性值構造 PropertyValue 實體物件

  18.            PropertyValue pv = new PropertyValue(propertyName, val);

  19.            parseMetaElements(ele, pv);

  20.            pv.setSource(extractSource(ele));

  21.            // 新增到 MutablePropertyValues 中

  22.            bd.getPropertyValues().addPropertyValue(pv);

  23.        }

  24.        finally {

  25.            this.parseState.pop();

  26.        }

  27.    }

與解析 constructor-arg 子元素步驟差不多。呼叫 parsePropertyValue() 解析子元素屬性值,然後根據該值構造 PropertyValue 實體物件並將其新增到 BeanDefinition 中的 MutablePropertyValues 中。

            

【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)

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

【死磕 Spring】----- IOC 之解析 bean 標簽:BeanDefinition

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

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

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

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

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

END

我是 Java 技術驛站,感謝有你

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖