【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】

  • 前言

  1. 前一篇博客我们讲到了对BeanDefinition的解析和载入,只是找到了方法,但具体的解析过程还没分析。这是本文的主要内容。
  2. Spring的源码刚开始看确实很痛苦,希望大家能埋头啃一遍,对于代码阅读能力和对Spring应用的问题解决能力绝对有相当大的提升。而且你再使用的时候,脑子里面不再是就该这么用,而是这样写它会怎么解析,除了这样还可以怎么怎么写。
  • BeanDefinition的载入和解析

  • <bean>及其子节点的解析

    前文讲了,这里写了所有Spring可以配置的属性、节点等等。比如我们常见的:<property>、<constructor-arg>等,也包括我们很少用的元信息(meta)、qualifier等,当然也有我们在bean里面配置的scope等。先看下简代码:

    代码1.1:BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法

       
       
    1. public AbstractBeanDefinition parseBeanDefinitionElement(
    2. Element ele, String beanName, BeanDefinition containingBean) {
    3. ...
    4. String className = null;
    5. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    6. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    7. }
    8. String parent = null;
    9. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    10. parent = ele.getAttribute(PARENT_ATTRIBUTE);
    11. }
    12. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    13. //这里面设置scope、abstract、lazy-init等
    14. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    15. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    16. parseMetaElements(ele, bd);
    17. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    18. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    19. parseConstructorArgElements(ele, bd);
    20. //设置property属性
    21. parsePropertyElements(ele, bd);
    22. parseQualifierElements(ele, bd);
    23. bd.setResource(this.readerContext.getResource());
    24. bd.setSource(extractSource(ele));
    25. ...
    26. }

    这里我们将两个比较有代表性的parseBeanDefinitionAttributes方法和parseConstructorArgElements方法。先来看第一个方法的代码。

    代码1.2:BeanDefinitionParserDelegate类的parseBeanDefinitionAttributes方法

       
       
    1. public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
    2. BeanDefinition containingBean, AbstractBeanDefinition bd) {
    3. //设置scope属性
    4. if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
    5. bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    6. if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
    7. error("Specify either 'scope' or 'singleton', not both", ele);
    8. }
    9. }
    10. else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
    11. bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
    12. BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
    13. }
    14. else if (containingBean != null) {
    15. //设置默认,也就是空
    16. bd.setScope(containingBean.getScope());
    17. }
    18. //设置abstract
    19. if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
    20. bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    21. }
    22. //设置lazy-init属性
    23. String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    24. if (DEFAULT_VALUE.equals(lazyInit)) {
    25. lazyInit = this.defaults.getLazyInit();
    26. }
    27. bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
    28. //设置自动装配属性
    29. String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
    30. bd.setAutowireMode(getAutowireMode(autowire));
    31. //设置dependency-check
    32. String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
    33. bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
    34. //设置depends-on
    35. if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
    36. String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
    37. bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));
    38. }
    39. //设置autowire-candidate
    40. String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
    41. if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
    42. String candidatePattern = this.defaults.getAutowireCandidates();
    43. if (candidatePattern != null) {
    44. String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
    45. bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
    46. }
    47. }
    48. else {
    49. bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
    50. }
    51. //设置primary
    52. if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
    53. bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
    54. }
    55. //init-method
    56. if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
    57. String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
    58. if (!"".equals(initMethodName)) {
    59. bd.setInitMethodName(initMethodName);
    60. }
    61. }
    62. else {
    63. if (this.defaults.getInitMethod() != null) {
    64. bd.setInitMethodName(this.defaults.getInitMethod());
    65. bd.setEnforceInitMethod(false);
    66. }
    67. }
    68. //设置destroy-method
    69. if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
    70. String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
    71. if (!"".equals(destroyMethodName)) {
    72. bd.setDestroyMethodName(destroyMethodName);
    73. }
    74. }
    75. else {
    76. if (this.defaults.getDestroyMethod() != null) {
    77. bd.setDestroyMethodName(this.defaults.getDestroyMethod());
    78. bd.setEnforceDestroyMethod(false);
    79. }
    80. }
    81. //设置工厂方法factory-method
    82. if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
    83. bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
    84. }
    85. //设置工厂类factory-bean
    86. if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
    87. bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
    88. }
    89. return bd;
    90. }

    上面这个方法是对<bean></bean>里面的配置,一般都是通过直接set的方式。有一点大家注意一下,赋值的是AbstractBeanDefinition抽象类,BeanDefinition是一个接口,AbstractBeanDefinition实现了该接口,并将一些赋值方法做了重写等,它是BeanDefinition的升级版

  • <property>元素的解析

    我们重点看parsePropertyElements方法。

    代码1.3:BeanDefinitionParserDelegate类的parsePropertyElements方法

       
       
    1. //该方法--调用入口
    2. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
    3. //获取<Bean>元素中所有的子元素
    4. NodeList nl = beanEle.getChildNodes();
    5. for (int i = 0; i < nl.getLength(); i++) {
    6. Node node = nl.item(i);
    7. //如果子元素是<property>子元素,则调用解析<property>子元素方法解析
    8. if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
    9. parsePropertyElement((Element) node, bd);
    10. }
    11. }
    12. }
    13. //该方法--解析<property>元素
    14. public void parsePropertyElement(Element ele, BeanDefinition bd) {
    15. //获取<property>元素的名字
    16. String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    17. if (!StringUtils.hasLength(propertyName)) {
    18. error("Tag 'property' must have a 'name' attribute", ele);
    19. return;
    20. }
    21. this.parseState.push(new PropertyEntry(propertyName));
    22. try {
    23. //如果一个Bean中已经有同名的property存在,则不进行解析,直接返回。
    24. //即如果在同一个Bean中配置同名的property,则只有第一个起作用
    25. if (bd.getPropertyValues().contains(propertyName)) {
    26. error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
    27. return;
    28. }
    29. //解析获取property的值
    30. Object val = parsePropertyValue(ele, bd, propertyName);
    31. //根据property的名字和值创建property实例
    32. PropertyValue pv = new PropertyValue(propertyName, val);
    33. //解析<property>元素中的属性
    34. parseMetaElements(ele, pv);
    35. pv.setSource(extractSource(ele));
    36. bd.getPropertyValues().addPropertyValue(pv);
    37. }
    38. finally {
    39. this.parseState.pop();
    40. }
    41. }

    我们继续看下解析获取property值的方法parsePropertyValue,该方法也适用于constructor arguments(构造器参数),只不过propertyName为空就行。来看代码:

    代码1.4:BeanDefinitionParserDelegate类的parsePropertyValue方法

       
       
    1. public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
    2. String elementName = (propertyName != null) ?
    3. "<property> element for property '" + propertyName + "'" :
    4. "<constructor-arg> element";
    5. //获取<property>的所有子元素,只能是其中一种类型:ref,value,list等
    6. NodeList nl = ele.getChildNodes();
    7. Element subElement = null;
    8. for (int i = 0; i < nl.getLength(); i++) {
    9. Node node = nl.item(i);
    10. //子元素不是description和meta属性
    11. if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
    12. !nodeNameEquals(node, META_ELEMENT)) {
    13. if (subElement != null) {
    14. error(elementName + " must not contain more than one sub-element", ele);
    15. }
    16. else {//当前<property>元素包含有子元素
    17. subElement = (Element) node;
    18. }
    19. }
    20. }
    21. //判断property的属性值是ref还是value,不允许既是ref又是value
    22. boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    23. boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    24. if ((hasRefAttribute && hasValueAttribute) ||
    25. ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
    26. error(elementName +
    27. " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
    28. }
    29. //如果属性是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
    30. if (hasRefAttribute) {
    31. String refName = ele.getAttribute(REF_ATTRIBUTE);
    32. if (!StringUtils.hasText(refName)) {
    33. error(elementName + " contains empty 'ref' attribute", ele);
    34. }
    35. //指向运行时所依赖对象 的引用
    36. RuntimeBeanReference ref = new RuntimeBeanReference(refName);
    37. //设置这个ref的数据对象是被当前的property对象所引用
    38. ref.setSource(extractSource(ele));
    39. return ref;
    40. }
    41. //如果属性是value,创建一个value的数据对象TypedStringValue,这个对象封装了value信息
    42. else if (hasValueAttribute) {
    43. //持有String类型值的对象 [这里一般我们都配class的路径,因为不实例化,所以这样处理]
    44. TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
    45. valueHolder.setSource(extractSource(ele));
    46. return valueHolder;
    47. }
    48. else if (subElement != null) {//如果当前<property>元素还有子元素
    49. return parsePropertySubElement(subElement, bd);
    50. }
    51. else {
    52. // 既不是"ref"又不是"value",报错
    53. error(elementName + " must specify a ref or value", ele);
    54. return null;
    55. }
    56. }

    通过对上述源码的分析,我们可以了解在Spring配置文件中,<Bean>元素中<property>元素的相关配置是如何处理的:
    (1). ref被封装为指向依赖对象一个引用。
    (2). value配置都会封装成一个字符串类型的对象。
    (3). ref和value都通过“解析的数据类型属性值.setSource(extractSource(ele))方法将属性值/引用与所引用的属性关联起来。
    (4).无论是ref/value/Collection,都只能有一个。

  • <property>子元素的解析

    在上面方法的最后对于<property>元素的子元素通过parsePropertySubElement方法解析,我们继续分析该方法的源码,了解其解析过程.
    代码1.5:BeanDefinitionParserDelegate类的parsePropertySubElement方法

       
       
    1. //该方法--调用入口
    2. public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
    3. return parsePropertySubElement(ele, bd, null);
    4. }
    5. //该方法--解析<property>元素中ref,value或者集合等子元素
    6. public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
    7. //如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析内嵌元素
    8. if (!isDefaultNamespace(ele)) {
    9. return parseNestedCustomElement(ele, bd);
    10. }
    11. //如果子元素是bean,则使用解析<Bean>元素的方法解析 【回到最初的那个bean解析方法】
    12. else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
    13. BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
    14. if (nestedBd != null) {
    15. nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
    16. }
    17. return nestedBd;
    18. }
    19. //如果子元素是ref,ref中只能有以下3个属性:bean、local、parent
    20. else if (nodeNameEquals(ele, REF_ELEMENT)) {
    21. //获取<property>元素中的bean属性值,引用其他解析的Bean的名称
    22. //【可以不再同一个Spring配置文件中】
    23. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
    24. boolean toParent = false;
    25. if (!StringUtils.hasLength(refName)) {
    26. //如果没有bean,获取<property>元素中的local属性值,引用同一个Xml文件中配置的Bean的id,
    27. //【local和ref不同,local只能引用同一个配置文件中的Bean】
    28. refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
    29. if (!StringUtils.hasLength(refName)) {
    30. //如果也没有local,获取<property>元素中parent属性值,引用父级容器中的Bean的id
    31. // 【parent只能引用同一个父级Context中的bean】.
    32. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
    33. toParent = true;
    34. if (!StringUtils.hasLength(refName)) {
    35. error("'bean', 'local' or 'parent' is required for <ref> element", ele);
    36. return null;
    37. }
    38. }
    39. }
    40. if (!StringUtils.hasText(refName)) {
    41. error("<ref> element contains empty target attribute", ele);
    42. return null;
    43. }
    44. /创建ref类型数据,指向被引用的对象
    45. RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
    46. ref.setSource(extractSource(ele));
    47. return ref;
    48. }
    49. //如果子元素是<idref>,使用解析ref元素的方法解析
    50. else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
    51. return parseIdRefElement(ele);
    52. }
    53. //如果子元素是<value>,使用解析value元素的方法解析
    54. else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
    55. return parseValueElement(ele, defaultValueType);
    56. }
    57. //如果子元素是null,为<property>设置一个封装null值的字符串数据
    58. else if (nodeNameEquals(ele, NULL_ELEMENT)) {
    59. TypedStringValue nullHolder = new TypedStringValue(null);
    60. nullHolder.setSource(extractSource(ele));
    61. return nullHolder;
    62. }
    63. //如果子元素是<array>,使用解析array集合子元素的方法解析
    64. else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
    65. return parseArrayElement(ele, bd);
    66. }
    67. //如果子元素是<list>,使用解析list集合子元素的方法解析
    68. else if (nodeNameEquals(ele, LIST_ELEMENT)) {
    69. return parseListElement(ele, bd);
    70. }
    71. //如果子元素是<set>,使用解析set集合子元素的方法解析
    72. else if (nodeNameEquals(ele, SET_ELEMENT)) {
    73. return parseSetElement(ele, bd);
    74. }
    75. //如果子元素是<map>,使用解析map集合子元素的方法解析
    76. else if (nodeNameEquals(ele, MAP_ELEMENT)) {
    77. return parseMapElement(ele, bd);
    78. }
    79. //如果子元素是<props>,使用解析props集合子元素的方法解析
    80. else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
    81. return parsePropsElement(ele);
    82. }
    83. else {
    84. error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
    85. return null;
    86. }
    87. }

    上面的代码我们可以学到哪些东西呢?Spring对上面这些子元素的解析,也就是我们可以给property配置的全部类型。如:bean,ref,idref,value,null,以及各种集合类。对于bean和ref在上面的方法中就有,直接创建一个引用的bean对象。

  • <list>子元素解析

    那么对于集合类型的,我们再看一个例子。
    代码1.6:BeanDefinitionParserDelegate类的parsePropertySubElement方法

       
       
    1. public List parseListElement(Element collectionEle, BeanDefinition bd) {
    2. //获取<list>元素中的value-type属性,即获取集合元素的数据类型
    3. String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
    4. //获取<list>集合元素中的所有子节点
    5. NodeList nl = collectionEle.getChildNodes();
    6. //Spring中将List封装为ManagedList
    7. ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
    8. target.setSource(extractSource(collectionEle));
    9. target.setElementTypeName(defaultElementType);
    10. target.setMergeEnabled(parseMergeAttribute(collectionEle));
    11. //具体的<list>元素解析
    12. parseCollectionElements(nl, target, bd, defaultElementType);
    13. return target;
    14. }
    15. //具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析
    16. protected void parseCollectionElements(
    17. NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
    18. //遍历集合所有节点
    19. for (int i = 0; i < elementNodes.getLength(); i++) {
    20. Node node = elementNodes.item(i);
    21. //节点不是description节点
    22. if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
    23. //将解析的元素加入集合中,递归调用下一个子元素
    24. target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
    25. }
    26. }
    27. }

    经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition【AbstractBeanDefinition】。那么还有其他属性的解析,大家可以自己看看。一定要多看看对于元素的解析,不然依赖注入的时候你整不明白这些属性从哪里来的。

  • BeanDefinition载入总结

    来一个代码的流程图,结合上一篇文章,顺便简单串一下主要方法。还有一个问题大家注意下,我们的代码一步步走下去是一点点细化的过程,从web.xml读取的可能是多个路径,一个路径下面可能有多个文件(Resource),一个文件里面有多个Bean,一个Bean里面有多个Property,一个Property里面只能有一个Ref,Value,List。这个过程大家要注意思考。我这里先放一张代码的流程图,再给大家举个比较复杂点的Bean的配置,大家结合着来看,可能会稍微好消化一点点。《【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】》     来看一个sessionFactory的配置,找了半天觉得这个很典型,基本上Spring源码里面准备解析的都有了。来看下代码

    <bean id="sessionFactory" class="com.xxx">
        	<!--dataSource属性指定要用到的数据源,因此在Hibernate的核心配置文件中就无需再配置与数据库连接相关的属性-->
        	<property name="dataSource" ref="xxxx" />
        	<property name="hibernateProperties">
    	        <props>			
    		     <!-- Hibernate基本配置 -->
    			<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
        			<prop key="hibernate.connection.pool_size">10</prop>
        			<prop key="hibernate.show_sql">true</prop>
        			<prop key="hibernate.format_sql">false</prop>
    		</props>
    	</property>
        	<!-- 加入使用注解的实体类 -->
        	<property name="annotatedClasses">
        	        <list>
        		        <value>com.xxx.xxx</value>
        			<value>com.xx.xxxx</value>
        		</list>
        	</property>
        </bean>

    我删了很多的配置,为了项目信息保密嘛,不过这个已经很有代表性了。我们可以看到Bean的基本配置,Property三种配置都有了,我们再结合上面对bean的解析、Property的解析、list的解析。  BeanDefinition的载入内容就先这么多,以后再有想法再补充吧!后面的注册就显得相对简单了,大家如果看到这里,注册基本不是问题。

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/u013185616/article/details/52224886
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞