【死磕 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技术驿站”,了解相关信息可以关注“Java技术驿站”。

相关文章
|
1天前
|
Java 容器 Spring
Javaweb之SpringBootWeb案例之 Bean管理的Bean作用域详细的解析
Javaweb之SpringBootWeb案例之 Bean管理的Bean作用域详细的解析
7 0
|
6天前
|
Java 数据库连接 开发者
浅谈Spring的Bean生命周期
浅谈Spring的Bean生命周期
15 1
|
11天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
19 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
11天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
28 2
从入门到精通:Spring基础注解的全面解析
|
11天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
25 0
切面编程的艺术:Spring动态代理解析与实战
|
11天前
|
Java 关系型数据库 MySQL
高级对象装配:解析Spring创建复杂对象的秘诀
高级对象装配:解析Spring创建复杂对象的秘诀
26 0
高级对象装配:解析Spring创建复杂对象的秘诀
|
20天前
|
XML Java 数据格式
Spring(一)IOC小案例
Spring(一)IOC小案例
|
21天前
|
XML Java 程序员
作为Java程序员还不知道Spring中Bean创建过程和作用?
作为Java程序员还不知道Spring中Bean创建过程和作用?
13 0
|
6天前
yolo-world 源码解析(六)(2)
yolo-world 源码解析(六)
16 0
|
6天前
yolo-world 源码解析(六)(1)
yolo-world 源码解析(六)
8 0

推荐镜像

更多