天天看點

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

上篇部落格( 【死磕 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. <bean id="bookService" class="org.springframework.core.service.BookService"/>

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

  13. <constructor-arg index="0" value="chenssy"/>

  14. <constructor-arg name="age" value="100"/>

  15. <constructor-arg name="bookService" ref="bookService"/>

  16. </bean>

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. "<property> element for property '" + propertyName + "'" :

  4. "<constructor-arg> 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 <ref> element", ele);

  25. return null;

  26. }

  27. }

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

  29. error("<ref> 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. <bean id="studentService" class="org.springframework.core.service.StudentService">

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

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

  4. </bean>

對于 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 中。

原文釋出時間為:2018-09-26

本文作者:Java技術驿站

本文來自雲栖社群合作夥伴“

Java技術驿站

”,了解相關資訊可以關注“

”。