Spring IoC容器初始化源码(3)—parseDefaultElement、parseCustomElement解析标签,registerBeanDefinition注册Bean定义【三万字】

  基于最新Spring 5.x,介绍了包括parseDefaultElement解析默认标签的方法、parseCustomElement解析扩展标签的方法和 registerBeanDefinition注册bean定义的方法的源码!

  上一篇文章:Spring IoC容器初始化源码(2)—prepareRefresh准备刷新、obtainFreshBeanFactory加载XML资源、解析<beans/>标签中,我们主要讲解了ClassPathXmlApplicationContext IoC容器初始化的refresh方法中的refresh()方法概述、prepareRefresh方法以及obtainFreshBeanFactory方法的前半部分的过程和源码,比如XML资源的加载,以及< beans/>标签的解析。
  现在我们继续向下学习refresh()方法,本篇文章的内容是接着上一篇讲解obtainFreshBeanFactory方法的后半部分,主要是parseDefaultElement解析默认标签的方法、parseCustomElement解析扩展标签的方法和registerBeanDefinition注册bean定义的方法的源码。

Spring IoC容器初始化源码 系列文章

Spring IoC容器初始化源码(1)—setConfigLocations设置容器配置信息

Spring IoC容器初始化源码(2)—prepareRefresh准备刷新、obtainFreshBeanFactory加载XML资源、解析<beans/>标签

Spring IoC容器初始化源码(3)—parseDefaultElement、parseCustomElement解析默认、扩展标签,registerBeanDefinition注册Bean定义

Spring IoC容器初始化源码(4)—<context:component-scan/>标签解析、spring.components扩展点、自定义Spring命名空间扩展点

Spring IoC容器初始化源码(5)—prepareBeanFactory、invokeBeanFactoryPostProcessors、registerBeanPostProcessors方法

Spring IoC容器初始化源码(6)—finishBeanFactoryInitialization实例化Bean的整体流程以及某些扩展点

Spring IoC容器初始化源码(7)—createBean实例化Bean的整体流程以及构造器自动注入

Spring IoC容器初始化源码(8)—populateBean、initializeBean实例化Bean以及其他依赖注入

< context:property-placeholder/>标签以及PropertySourcesPlaceholderConfigurer占位符解析器源码深度解析

三万字的ConfigurationClassPostProcessor配置类后处理器源码深度解析

基于JavaConfig的AnnotationConfigApplicationContext IoC容器初始化源码分析

文章目录

  • Spring IoC容器初始化源码 系列文章
  • 1 parseDefaultElement解析默认标签
    • 1.1 processBeanDefinition解析< bean/>标签注册Bean定义
      • 1.1.1 parseBeanDefinitionElement解析< bean/>标签获取BeanDefinitionHolder
        • 1.1.1.1 checkNameUniqueness校验beanName唯一性
        • 1.1.1.2 parseBeanDefinitionElement解析< bean/>标签获取bean定义
          • 1.1.1.2.1 createBeanDefinition创建BeanDefinition
          • 1.1.1.2.2 parseBeanDefinitionAttributes解析< bean/>标签的各种属性
          • 1.1.1.2.3 parseConstructorArgElements解析所有< constructor-arg/>子标签
            • 1.1.1.2.3.1 parseConstructorArgElement解析一个< constructor-arg/>子标签
          • 1.1.1.2.4 parsePropertyElements解析所有< property>子标签
            • 1.1.1.2.4.1 parsePropertyElement解析一个< property/>子标签
          • 1.1.1.2.5 parsePropertyValue解析标签元素的值
            • 1.1.1.2.5.1 parsePropertySubElement解析值子标签
            • 1.1.1.2.5.1.1 parseValueElement解析< value/>值标签
            • 1.1.1.2.5.1.2 解析< array/>、< list/>、< set/>值标签
            • 1.1.1.2.5.1.3 parseMapElement解析< map/>值标签
            • 1.1.1.2.5.1.4 解析< props/>集合值标签
      • 1.1.2 registerBeanDefinition注册Bean定义
        • 1.1.2.1 registerBeanDefinition注册BeanDefinition
          • 1.1.2.1.1 resetBeanDefinition重置BeanDefinition缓存
        • 1.1.2.2 registerAlias注册别名映射
    • 1.2 importBeanDefinitionResource解析< import/>标签注册bean定义
    • 1.3 processAliasRegistration解析< alias/>标签注册别名映射
  • 2 parseCustomElement解析扩展标签
    • 2.1 resolve解析namespaceUri
      • 2.1.1 getHandlerMappings获取NamespaceHandler映射
      • 2.1.2 init初始化标签节点解析/装饰器
        • 2.1.2.1 registerBeanDefinitionParser、registerBeanDefinitionDecorator注册bean定义解析/装饰器
    • 2.2 parse解析自定义标签节点
  • 3 小结

1 parseDefaultElement解析默认标签

  parseDefaultElement被上一篇文章的末尾的方法parseBeanDefinitions方法调用,用于解析< beans/>根标签下的默认命名空间(xmlns=“http://www.springframework.org/schema/beans”)下的标签,就是四个标签: < import/>、< alias/>、< bean/>、< beans/>。

  1. < import/>标签表示引入其他XML文件,使用importBeanDefinitionResource方法解析;
  2. < alias/>标签表示设置bean别名,使用processAliasRegistration方法解析;
  3. < bean/>标签表示一个bean定义,使用processBeanDefinition方法解析,这个是核心标签。
  4. < beans/>标签,表示嵌套的< beans/>标签,那么递归的调用doRegisterBeanDefinitions方法解析,这个方法在上一篇文章中已经介绍了。
//-----DefaultBeanDefinitionDocumentReader的常量属性

/**
 * 标签名常量
 */
public static final String NESTED_BEANS_ELEMENT = "beans";

/**
 * 标签名常量
 */
public static final String ALIAS_ELEMENT = "alias";

/**
 * 标签名常量
 */
public static final String IMPORT_ELEMENT = "import";

/**
 * 引用BeanDefinitionParserDelegate的常量,表示标签名"bean"
 */
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

/**
 1. DefaultBeanDefinitionDocumentReader的方法
 2. 

3. 解析beans根标签下的默认命名空间(xmlns="http://www.springframework.org/schema/beans")下的标签 4. 就是四个标签: 5. 6. @param ele 文档标签 7. @param delegate 解析器 */ private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果是标签,表示引入其他XML文件 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //如果是标签,表示bean别名 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //如果是标签,表示bean定义 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //如果是标签,表示嵌套的标签 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { //递归的调用doRegisterBeanDefinitions方法解析标签 doRegisterBeanDefinitions(ele); } }

1.1 processBeanDefinition解析< bean/>标签注册Bean定义

  processBeanDefinition方法位于DefaultBeanDefinitionDocumentReader类中,但是方法内部还是委托BeanDefinitionParserDelegate解析器来解析的。
  processBeanDefinition方法主要做了四件事:

  1. 核心方法。调用BeanDefinitionParserDelegate.parseBeanDefinitionElement方法将< bean/>标签对应的Element元素对象,解析为bean定义——BeanDefinition,返回BeanDefinitionHolder对象bdHolder,这个holder对象封装了“bean定义”——BeanDefinition、bean的名称——beanName以及bean别名数组——aliasesArray。
  2. 继续调用BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired方法。对于< bean/>标签下的自定义属性和子标签进行解析,基本不会出现自定义属性和子标签的情况。自定义属性就是除了< bean/>标签自带的属性之外的属性,自定义子标签就是非默认命名空间下的标签。
  3. 核心方法。调用BeanDefinitionReaderUtils.registerBeanDefinition方法将解析之后的BeanDefinition注册到Registry中,这个Registry实际上就是上下文容器内部的DefaultListableBeanFactory实例。
  4. 最后发布注册事件,通知相关的监听器,这个Bean已经加载完成了。这是一个空实现,留给子类扩展。

  我们主要学习第一步:< bean/>标签的解析获取BeanDefinition的过程,以及第三步:注册BeanDefinition的过程。

/**
 * DefaultBeanDefinitionDocumentReader的方法
 * 

* 处理给定的标签,解析bean定义并将其注册到注册表 * * @param ele 标签元素结点 * @param delegate 解析器 */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { /* * 1 核心实现。解析标签对象,解析完成之后转换为一个BeanDefinition(BeanDefinitionHolder)对象 */ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { /* * 2 对于标签下的自定义属性和子标签进行解析,基本不会出现自定义属性和子标签的情况,略过 * 自定义属性就是除了标签自带的属性之外的属性,自定义子标签就是非默认命名空间下的标签 */ bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { /* * 3 核心实现。将解析之后的bean定义,注册到Register中 * 这个Registry实际上就是上下文内部的DefaultListableBeanFactory工厂实例 */ BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } /* * 4 最后发出注册事件,通知相关的监听器,这个Bean已经加载完成了 * 默认eventListener为EmptyReaderEventListener,它的方法都是空实现,留给子类扩展 */ getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

1.1.1 parseBeanDefinitionElement解析< bean/>标签获取BeanDefinitionHolder

  我们首先看delegate解析器的parseBeanDefinitionElement方法,从方法名字也能看出来,它的目的就是解析< bean/>标签,并且对解析出来的各种值进行处理后封装成为bean定义对象—BeanDefinition,最后将BeanDefinition、bean的名称- beanName以及bean别名-aliasesArray封装成BeanDefinitionHolder返回。
  解析< bean/>标签实际上就是解析< bean/>标签的各种属性、值以及子标签,而我们此前通过SAX解析出来的Element对象就封装了一个标签的全部属性和值,这里只需要提取出来然后做自己的逻辑就行了。
  BeanDefinitionParserDelegate中定义了几乎全部的标签属性名常量。
  parseBeanDefinitionElement的大概步骤如下:

  1. 解析、校验< bean/>标签的id和name属性。
    1. 将name值根据","、";"、" "拆分成为一个别名数组aliases。将id设置为beanName。
    2. 如果没有设置id属性,那么将第一个别名从aliases移除并作为beanName。
    3. 对于外部bean,调用checkNameUniqueness方法校验beanName的唯一性
  2. 调用另一个parseBeanDefinitionElement方法,解析、检验 < bean/>标签的其他属性和子标签并且统一封装至一个GenericBeanDefinition实例beanDefinition中,这是核心方法(使用组件注解的bean定义将被封装成ScannedGenericBeanDefinition,使用@Bean注解的bean定义将被封装成ConfigurationClassBeanDefinition)。
  3. 如果beanDefinition不为null,那么继续处理beanName,如果没有设置id或者name属性,那么beanName就是空,此时需要按照Spring提供的规则自己生成beanName了,我们在IoC学习部分已经讲过了,但是不全面,这里我们完整的表达一下:
    1. 如果没有设置id,那么使用设置的第一个name属性作为beanName;
    2. 如果没有设置id和name,那么使用Spring的规则生成beanName,采用的生成器是DefaultBeanNameGenerator:
      1. 对于内部bean:
        1. 内部bean的beanName生成规则就是:“类的全路径名#当前beanDefinition对象的一致性hash码的16进制表示”
        2. 即#后面的字符通过"Integer.toHexString(System.identityHashCode(beanDefinition)) "公式生成。由于采用了一致性哈希值,因此生成的beanName不会重复。
      2. 对于外部bean:
        1. 外部bean的beanName生成规则就是:“类的全路径名#0”、“类的全路径名#1”、“类的全路径名#2”……
        2. 有n个没有命名的同类型外部bean,那么名字后面就是从[0,n-1]类似于索引递增的进行命名,如果中途遇到同名bean,那么跳过这个索引,使用下一个。
        3. 将类的全路径名作为别名。
  4. 最后将bean定义——BeanDefinition、bean名字——beanName、bean别名数组——aliasesArray封装至一个BeanDefinitionHolder对象中返回。

  另外,方法中的containingBean属性,在很多文章中都没有介绍,这实际上和内部bean有关。containingBean表示当前的bean的包含bean,如果当前标签元素对应一个外部bean,那么该属性为null;如果当前标签元素对应一个内部bean,那么该属性就是它的外部bean的BeanDefinition。
  内部bean是什么?这相当于一个专属于某个bean属性bean,或者类似于匿名bean,为该标签设置的name、id、scope属性都是没意义的,将会使用外部bean的属性,但是仍然会生成beanName。在我们IoC学习的部分有详细的案例,在此不做赘述!

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析提供的标签元素。如果解析错误,则可能会返回null * 这个方法解析的标签元素都是代表外部bean */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { //调用另一个parseBeanDefinitionElement方法,containingBean参数传递null return parseBeanDefinitionElement(ele, null); } //-----BeanDefinitionParserDelegate的相关属性----- /** * 一些属性值的分隔符号常量:","、";"、" " * 比如name属性就可以使用分隔符表示定义多个属性 */ public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; "; /** * id属性名常量 */ public static final String ID_ATTRIBUTE = "id"; /** * name属性名常量 */ public static final String NAME_ATTRIBUTE = "name"; /** * BeanDefinitionParserDelegate的方法 *

* 解析提供的标签element元素。如果解析错误,则可能会返回null *

* 直接使用element对象的相关方法即可获取对应标签的属性值 * * @param ele 标签元素对象 * @param containingBean 当前的bean的包含bean。 * 如果当前标签元素对应一个外部bean,那么该属性为null, * 如果当前标签元素对应一个内部bean,那么该属性就是它的外部bean的BeanDefinition */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { /* * 1 解析、检验 id和name属性 */ //获取id属性的值id String id = ele.getAttribute(ID_ATTRIBUTE); //获取name属性的值nameAttr String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //别名集合 List<String> aliases = new ArrayList<>(); /* * 如果nameAttr不是null也不是"",那么根据","、";"、" "拆分nameAttr为别名数组 */ if (StringUtils.hasLength(nameAttr)) { //将 nameAttr 根据","、";"、" "拆分成为一个别名数组 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); //添加到别名集合aliases中 aliases.addAll(Arrays.asList(nameArr)); } //beanName默认设置为id String beanName = id; //如果没有指定id属性,并且定义了name别名属性 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { //移除aliases列表的第一个字符串,将其作为beanName,其他的仍然作为别名 beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } //如果是外部bean,进行唯一性校验 //内部bean不会检查,实际上内部bean的id 和name属性都没有意义,这一点我们在IoC的学习部分已经介绍了 if (containingBean == null) { /* * 检查当前标签的名字和别名是否被所属的标签下的其他bean使用了 * 如果有重复的名字或者别名那么直接抛出异常, * 否则将正在解析的名字和别名加入到记录当前标签下所有bean name的set集合usedNames中 */ checkNameUniqueness(beanName, aliases, ele); } /* * 2 解析、检验 标签的其他属性以及子标签并且统一封装至一个GenericBeanDefinition实例中返回 * 这一步完毕,bean定义 --> BeanDefinition,已经创建好了 */ AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); /* * 3 如果返回的beanDefinition不为null,那么继续处理beanName, * 并将解析出来的 bean定义(BeanDefinition)封装至一个BeanDefinitionHolder实例中,最后返回 */ if (beanDefinition != null) { //如果没有设置id或者name属性,那么beanName就是空,此时需要按照Spring提供的规则自己生成beanName了 if (!StringUtils.hasText(beanName)) { try { //如果是内部bean if (containingBean != null) { /* * 通过BeanDefinitionReaderUtils.generateBeanName方法生成内部bean的beanName * 如果没有指定id和name,内部bean的beanName生成规则就是:"类的全路径名#当前beanDefinition对象的一致性hash码的16进制表示" * * 即#后面的字符通过:"Integer.toHexString(System.identityHashCode(beanDefinition))"生成 * 由于采用了一致性哈希值,因此生成的beanName不会重复 */ beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } //如果是外部bean else { /* * 通过XmlReaderContext生成beanName,基于XML配置的beanName生成规则我们在IOC的学习部分已经讲过: * 1 如果没有指定id和name,那么将使用"类的全路径名#0"、"类的全路径名#1"、"类的全路径名#2"……的方式来为bean命名 * 2 有n个没有命名的同类型bean,那么名字后面就是从[0,n-1]类似于索引递增的进行命名,如果中途遇到同名bean,那么跳过这个索引,使用下一个。 * 实际上XmlReaderContext.generateBeanName内部就是调用DefaultBeanNameGenerator.generateBeanName方法 * 因此最终和内部bean一样都是使用BeanDefinitionReaderUtils.generateBeanName生成的beanName,只是第三个参数为false */ beanName = this.readerContext.generateBeanName(beanDefinition); //获取全路径类名 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { //beanClassName作为bean的别名 aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } //aliases转换为数组 String[] aliasesArray = StringUtils.toStringArray(aliases); //将BeanDefinition、beanName、aliasesArray封装至一个BeanDefinitionHolder对象中返回 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } //beanDefinition为null,那么返回null return null; }

1.1.1.1 checkNameUniqueness校验beanName唯一性

  BeanDefinitionParserDelegate内部具有一个usedNames的集合,存储了当前< beans/>标签下所有使用的 bean 名称和别名,以便检查每个 bean 元素的唯一性。一个< beans/>标签对应一个BeanDefinitionParserDelegate解析器。
  checkNameUniqueness方法用于从usedNames集合中检查当前的bean的名字和别名的唯一性,如果名字和别名在同一个< beans/>标签内不唯一,那么直接抛出异常。如果唯一,则将当前的bean的名字和别名加入到usedNames集合中,为后面的bean的唯一性检查所使用。

/**
 * BeanDefinitionParserDelegate的属性
 * 

* 存储一个标签下所有使用的 bean 名称和别名,以便检查每个 bean 元素的唯一性。 */ private final Set<String> usedNames = new HashSet<>(); /** * BeanDefinitionParserDelegate的方法 *

* 检查当前标签的名字和别名是否被所属的标签下的其他bean使用了 * 如果被使用了那么直接抛出异常 * * @param aliases bean别名数组 * @param beanElement 标签元素对象 * @param beanName bean名字 */ protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) { //被找到的name String foundName = null; //如果beanName不为空并且usedNames已经包含了这个beanName if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) { //foundName赋值给beanName foundName = beanName; } //如果没有找到beanName,那么继续找aliases if (foundName == null) { //获取第一个找到的name赋值给beanName foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases); } //如果foundName(被找到的name)属性不为null,说明有重复的名字和别名,直接抛出异常 if (foundName != null) { error("Bean name '" + foundName + "' is already used in this element", beanElement); } //到这里,表示没找到重复的,将正在解析的bean的名字和别名添加进去 this.usedNames.add(beanName); this.usedNames.addAll(aliases); }

1.1.1.2 parseBeanDefinitionElement解析< bean/>标签获取bean定义

  parseBeanDefinitionElement(ele,beanName,containingBean)方法被前面的parseBeanDefinitionElement(ele,containingBean)方法调用。
  该方法用于解析 < bean/>标签的其他属性(不考虑名称或别名,因此此前已经解析过了),以及内部的其他子标签,并且将解析结果统一封装至一个GenericBeanDefinition实例中返回,如果在解析期间出现问题,则可能会返回null。
  我们主要学习createBeanDefinition、parseBeanDefinitionAttributes、parseConstructorArgElement、parsePropertyElement这几个方法。

//-----BeanDefinitionParserDelegate的相关属性-----

public static final String CLASS_ATTRIBUTE = "class";

public static final String PARENT_ATTRIBUTE = "parent";

public static final String DESCRIPTION_ELEMENT = "description";


/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析标签的其他属性和子标签,不考虑名称或别名,并且统一封装至一个GenericBeanDefinition实例中 * 如果在解析期间出现问题,则可能会返回null * * @param ele 需要被解析的标签 * @param beanName bean名字,可能为空 * @param containingBean 当前的bean的包含bean定义 */ @Nullable public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { //记录解析过程,开始 this.parseState.push(new BeanEntry(beanName)); /*1 解析class属性*/ String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { //去除前后空白 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } /*2 解析parent属性*/ String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { /*3 使用给定的bean className和bean parentName属性创建BeanDefinition对象。*/ //BeanDefinition的实际类型是GenericBeanDefinition类型 AbstractBeanDefinition bd = createBeanDefinition(className, parent); /*4 标签元素的各种属性设置到bd中*/ parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //设置标签的description描述属性 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); /*5 解析标签的子标签*/ //解析子标签,很少用 parseMetaElements(ele, bd); //解析子标签,用于查找方法注入,很少用 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析子标签,用于任意方法替换,很少用 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析子标签,表示一个构造器参数 parseConstructorArgElements(ele, bd); //解析子标签,表示一个setter方法注入 parsePropertyElements(ele, bd); //解析子标签,用于自动注入,很少用 parseQualifierElements(ele, bd); //设置资源和依赖的资源 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); //返回bd return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { //记录解析过程,弹出 this.parseState.pop(); } //返回null return null; }

1.1.1.2.1 createBeanDefinition创建BeanDefinition

  createBeanDefinition使用给定的bean className和bean parentName属性创建BeanDefinition对象。
  BeanDefinition的实际类型是GenericBeanDefinition类型。BeanDefinition对象非常重要,这就是XML中的“bean定义”在Java中的抽象!我们常说的bean定义就是指XML中定义的某个bean在Java中对应的BeanDefinition对象!

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 使用给定的bean className和bean parentName属性创建BeanDefinition对象。 * BeanDefinition的实际类型是GenericBeanDefinition类型 * * @param className bean的class属性值 * @param parentName bean的parent属性值 * @return 新创建的 bean 定义(BeanDefinition) * @throws ClassNotFoundException 解析bean的class失败 */ protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException { //调用BeanDefinitionReaderUtils工具类的方法 return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); } /** * BeanDefinitionReaderUtils的方法 *

* 使用给定的bean className和bean parentName属性创建GenericBeanDefinition对象。 * * @param parentName bean的parent属性值 * @param className bean的class属性值 * @param classLoader 用于加载 bean 类的类加载器,可以为null(按名称注册 bean class ) * @return bean 定义 * @throws ClassNotFoundException 如果无法加载 bean 的class */ public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { //创建GenericBeanDefinition对象 GenericBeanDefinition bd = new GenericBeanDefinition(); //设置parentName属性 bd.setParentName(parentName); //如果className不为null if (className != null) { //类加载器不为null if (classLoader != null) { //通过全路径的className获取Class对象,并设置到beanClass属性中 bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { //将className直接设置到beanClass属性中 bd.setBeanClassName(className); } } //返回已创建GenericBeanDefinition对象 return bd; }

1.1.1.2.2 parseBeanDefinitionAttributes解析< bean/>标签的各种属性

  这一步就是机械化的解析< bean/>标签剩下属性,并将结果同样封装到该bean对应的BeanDefinition对象中。
  在此前createDelegate方法创建解析器的时候,就已经解析了外部< beans/>根标签的属性,并存入defaults属性对象中。在parseBeanDefinitionAttributes方法中某些属性如果没有设置值,那么将使用外部< beans/>标签的属性值。如果当前bean自己设置了值,那么就不会使用外部< beans/>根标签的属性,这就是我们在IoC学习过程中所说的一些属性设置覆盖原则。

//-----BeanDefinitionParserDelegate的部分属性-----

private static final String SINGLETON_ATTRIBUTE = "singleton";

public static final String SCOPE_ATTRIBUTE = "scope";

public static final String ABSTRACT_ATTRIBUTE = "abstract";

public static final String TRUE_VALUE = "true";

public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";

public static final String AUTOWIRE_ATTRIBUTE = "autowire";

public static final String DEPENDS_ON_ATTRIBUTE = "depends-on";

public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";

public static final String PRIMARY_ATTRIBUTE = "primary";

public static final String INIT_METHOD_ATTRIBUTE = "init-method";

public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";

public static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";

public static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";

/**
 * 分隔符号:","、";"、" "
 */
public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";


/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析标签元素的各种属性,并且设置给当前的bean定义(BeanDefinition) *

*

* 在此前createDelegate方法创建解析器的时候,就已经解析了外部根标签的属性,并存入defaults属性对象中 * 在parseBeanDefinitionAttributes方法中某些属性如果没有设置值,那么将使用外部标签的属性值 * 如果当前bean自己设置了值,那么就不会使用外部根标签的属性,这就是我们在IoC学习过程中所说的一些属性设置覆盖原则 * * @param ele 标签元素 * @param beanName bean名字 * @param containingBean 当前的bean的包含bean定义 * @param bd 当前bean的bean定义 * @return 设置个各种属性的BeanDefinition */ public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { /*如果具有singleton属性*/ if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { //抛出异常,singleton属性在1.x版本之后已经不被支持,使用scope属性代替 //error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } /*否则,如果具有scope属性*/ else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { //设置scope属性值 bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } /*否则,当前bean是一个内部bean,进行内部bean处理*/ else if (containingBean != null) { //内部bean的定义接收外部包含bean定义的属性 //这里能够看出来,内部bean自己设置scope属性值是没意义的 bd.setScope(containingBean.getScope()); } //如果具有abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { //设置abstract属性 bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } //获取lazy-init属性的值lazyInit String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); //如果是默认值或者是"" if (isDefaultValue(lazyInit)) { //那么使用外部标签的属性值 lazyInit = this.defaults.getLazyInit(); } //设置lazyInit属性 bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); //获取autowire属性的值autowire String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); //设置autowire属性模式 bd.setAutowireMode(getAutowireMode(autowire)); //如果设置了depends-on属性值(depends-on属性没有默认值) if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { //获取depends-on属性的值dependsOn String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); //设置dependsOn属性,使用","、";"、" "拆分成为一个数组 bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } //获取autowire-candidate属性值autowireCandidate String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); //如果是默认值或者是"" if (isDefaultValue(autowireCandidate)) { //那么获取外部标签的default-autowire-candidate属性值 String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { //使用","拆分为数组 String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); //是否匹配当前bean的名字,如果匹配,那么设置为true,否则设置为false bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } //如果设置了值,如果是字符串值是"true",那么属性设置为true,否则设置为false else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } //如果设置了primary属性值(primary属性没有默认值) if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { //如果是字符串值是"true",那么属性设置为true,否则设置为false bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //如果设置了init-method属性值(init-method属性没有默认值) if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { //设置该值 String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } //否则,如果外部标签的default-init-method属性不为null else if (this.defaults.getInitMethod() != null) { //那么设置为外部标签的default-init-method属性值 bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } //如果设置了destroy-method属性值(destroy-method属性没有默认值) if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { //设置该值 String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } //否则,如果外部标签的default-destroy-method属性不为null else if (this.defaults.getDestroyMethod() != null) { //那么设置为外部标签的default-destroy-method属性值 bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } //如果设置了factory-method属性值(factory-method属性没有默认值) if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { //设置该值 bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //如果设置了factory-bean属性值(factory-bean属性没有默认值) if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { //设置该值 bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } //将标签对应的element元素中的属性获取出来经过处理之后设置到了当前bean的BeanDefinition之后即可返回 return bd; } /** * 某些属性的默认值 */ public static final String DEFAULT_VALUE = "default"; /** * 某个属性的值是否是默认值或者是"" */ private boolean isDefaultValue(String value) { return (DEFAULT_VALUE.equals(value) || "".equals(value)); } public static final String AUTOWIRE_BY_NAME_VALUE = "byName"; public static final String AUTOWIRE_BY_TYPE_VALUE = "byType"; public static final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor"; public static final String AUTOWIRE_AUTODETECT_VALUE = "autodetect"; private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition(); /** * 将给定的autowire属性的String类型的属性值转换为int类型的属性mode * 默认不0自动注入,除非XML设置 */ @SuppressWarnings("deprecation") public int getAutowireMode(String attrValue) { String attr = attrValue; //如果是默认值或者是"" if (isDefaultValue(attr)) { //那么使用外部标签的属性值 attr = this.defaults.getAutowire(); } //默认0,不自动注入 int autowire = AbstractBeanDefinition.AUTOWIRE_NO; //其他模式, byName if (AUTOWIRE_BY_NAME_VALUE.equals(attr)) { autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME; } //byType else if (AUTOWIRE_BY_TYPE_VALUE.equals(attr)) { autowire = AbstractBeanDefinition.AUTOWIRE_BY_TYPE; } //constructor else if (AUTOWIRE_CONSTRUCTOR_VALUE.equals(attr)) { autowire = AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR; } //autodetect 被废弃了 else if (AUTOWIRE_AUTODETECT_VALUE.equals(attr)) { autowire = AbstractBeanDefinition.AUTOWIRE_AUTODETECT; } // 否则返回值默认值 return autowire; }

1.1.1.2.3 parseConstructorArgElements解析所有< constructor-arg/>子标签

  首先获取< bean/>标签元素下面的所有子节点元素,然后循环遍历,如果如果当前节点是一个标签节点并且标签名为"constructor-arg",那么继续调用parseConstructorArgElement方法解析该< constructor-arg/>标签。
  < constructor-arg/>标签对应着构造器注入的标签,解析后的结果将统一封装到BeanDefinition中的ConstructorArgumentValues属性中。

/**
 * BeanDefinitionParserDelegate的属性
 * 子标签名常量
 */
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";

/**
 1. BeanDefinitionParserDelegate的方法
 2. 

3. 解析标签元素下的全部子标签元素 4. 5. @param beanEle 标签元素 6. @param bd 当前标签元素对应的BeanDefinition */ public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { //获取标签元素下的所有子节点元素 NodeList nl = beanEle.getChildNodes(); //遍历 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果当前节点是一个Element标签节点并且标签名为"constructor-arg" if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { //那么解析这个子标签 parseConstructorArgElement((Element) node, bd); } } }

1.1.1.2.3.1 parseConstructorArgElement解析一个< constructor-arg/>子标签

  parseConstructorArgElement用于解析一个< constructor-arg/>子标签,并将相关属性和值存入当前< bean/>标签元素对应的BeanDefinition的ConstructorArgumentValues属性中。
  大概步骤为:

  1. 首先解析index、type、name属性,获取值indexAttr、typeAttr、nameAttr;
  2. 如果indexAttr的值不为null也不为””,即设置了index索引属性
    1. indexAttr尝试转换为数值index,如果无法转换或者转换之后的值小于0,那么抛出异常。
    2. 调用parsePropertyValue方法解析标签的值(值可能来自value属性、ref属性、或者子标签)并返回解析结果。
    3. 将返回的值解析结果、typeAttr、nameAttr等属性封装到一个ConstructorArgumentValues.ValueHolder类型的对象valueHolder中。
    4. 从目前的BeanDefinition中查找,判断当前的index是否已存在:
      1. 如果存在,说明index属性重复,抛出异常。
      2. 如果不存在,那么正常。将当前< constructor-arg/>子标签的解析结果以key-value的形式封装(key就是index,value就是valueHolder)封装到BeanDefinition中的ConstructorArgumentValues属性中的indexedArgumentValues属性中。
  3. 如果indexAttr的值为null或者”” ,即没设置index属性
    1. 调用parsePropertyValue方法解析标签的值(值可能来自value属性、ref属性、或者子标签)并返回解析结果。
    2. 将返回的值解析结果、typeAttr、nameAttr等属性封装到一个ConstructorArgumentValues.ValueHolder类型的对象valueHolder中。
    3. 将当前< constructor-arg/>子标签的解析结果valueHolder封装到BeanDefinition中的ConstructorArgumentValues属性中的genericArgumentValues属性中。
    4. 最后返回BeanDefinition,已经封装了当前< constructor-arg>标签的解析结果。

  从源码可知,所有的< constructor-arg/>子标签的结果都存入BeanDefinition中的ConstructorArgumentValues属性中。其中具有index索引的以“index-valueHolder”的形式存入该属性对象内部的indexedArgumentValues属性中,就是一个Map;没有设置index索引的以单个valueHolder的形式存入该属性对象内部的genericArgumentValues属性中,就是一个List。

//--------BeanDefinitionParserDelegate的相关属性---------

public static final String INDEX_ATTRIBUTE = "index";

public static final String TYPE_ATTRIBUTE = "type";

public static final String NAME_ATTRIBUTE = "name";


/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析一个标签元素 * 将解析后的结果封装到BeanDefinition中的ConstructorArgumentValues属性中 * * @param ele 子标签元素 * @param bd 当前标签元素对应的BeanDefinition */ public void parseConstructorArgElement(Element ele, BeanDefinition bd) { //获取index、type、name属性的值indexAttr、typeAttr、nameAttr String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); /*如果indexAttr不为null也不为"",即设置了标签的index属性值*/ if (StringUtils.hasLength(indexAttr)) { try { //解析为Integer类型 int index = Integer.parseInt(indexAttr); //小于0就抛出异常 if (index < 0) { error("'index' cannot be lower than 0", ele); } //大于等于0 else { try { //记录解析状态 this.parseState.push(new ConstructorArgumentEntry(index)); /*核心代码。解析子标签元素的值,可能是value属性、ref属性、或者子标签,返回相应的值封装对象*/ Object value = parsePropertyValue(ele, bd, null); //解析的value使用ConstructorArgumentValues.ValueHolder类型的valueHolder对象包装 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); //如果type属性不为null也不是"" if (StringUtils.hasLength(typeAttr)) { //封装type属性到valueHolder valueHolder.setType(typeAttr); } //如果name属性不为null也不是"" if (StringUtils.hasLength(nameAttr)) { //封装name属性到valueHolder valueHolder.setName(nameAttr); } //设置源 valueHolder.setSource(extractSource(ele)); /* * 从BeanDefinition中查找,判断当前index是否已存在 * 如果存在,说明index属性重复,抛出异常 */ if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } /* * 如果不存在,那么正常 */ else { //将当前constructor-arg子标签的解析结果以key-value的形式封装,key就是index,value就是valueHolder //封装到BeanDefinition中的ConstructorArgumentValues属性中的indexedArgumentValues属性中 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { //无法转换为数值,抛出异常 error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } /*如果indexAttr为null或者"",即没有设置标签的index属性值*/ else { try { this.parseState.push(new ConstructorArgumentEntry()); /*核心代码。解析子标签元素的值,可能来自于是value属性、ref属性、或者子标签,返回相应的值封装对象*/ Object value = parsePropertyValue(ele, bd, null); //解析的value使用ConstructorArgumentValues.ValueHolder类型的valueHolder对象包装 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); //如果type属性不为null也不是"" if (StringUtils.hasLength(typeAttr)) { //封装type属性到valueHolder valueHolder.setType(typeAttr); } //如果name属性不为null也不是"" if (StringUtils.hasLength(nameAttr)) { //封装name属性到valueHolder valueHolder.setName(nameAttr); } //设置源 valueHolder.setSource(extractSource(ele)); //将当前子标签的解析结果封装到BeanDefinition中的 //ConstructorArgumentValues属性中的genericArgumentValues属性中 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } }

1.1.1.2.4 parsePropertyElements解析所有< property>子标签

  首先获取< bean/>标签元素下面的所有子标签元素,然后循环遍历,如果如果当前节点是一个标签节点并且标签名为"property",那么继续调用parsePropertyElement方法解析该< property/>标签。
  < property/>标签对应着我们手动设置的setter方法注入的标签,解析后的结果将统一封装到BeanDefinition中的propertyValues属性中。

/**
 * BeanDefinitionParserDelegate的属性
 * 子标签名常量
 */
public static final String PROPERTY_ELEMENT = "property";

/**
 1. BeanDefinitionParserDelegate的方法
 2. 

3. 解析标签元素下的全部子标签元素 4. 5. @param beanEle 标签元素 6. @param bd 当前标签元素对应的BeanDefinition */ public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //获取标签元素下的所有子节点标签元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果当前节点是一个Element标签节点并且标签名为"property" if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { //那么解析这个子标签 parsePropertyElement((Element) node, bd); } } }

1.1.1.2.4.1 parsePropertyElement解析一个< property/>子标签

  parsePropertyElement用于解析一个< property/>子标签,并将相关属性和值存入当前< bean/>标签元素对应的BeanDefinition的propertyValues属性中。
  大概步骤为:

  1. 首先解析name属性,获取属性名称propertyName。如果propertyName值为null或者"",那么抛出异常。
  2. 继续校验如果当前< bean/>标签元素对应的BeanDefinition的propertyValues属性中包含了当前propertyName,即name重复,那么抛出异常。
  3. 调用parsePropertyValue方法,解析< property/>子标签元素的值,值可能来自value属性、ref属性、或者子标签,返回相应的值封装对象。
    1. 这个方法和解析constructor-arg标签的代码中的值解析是调用的同一个方法,区别是propertyName属性不为null,而这个propertyName属性仅仅是为了异常日志打印而已,没有其他作用。
  4. 将属性名propertyName和解析出来的值封装成为一个PropertyValue类型的对象pv,将pv封装到BeanDefinition中的propertyValues属性中,实际上是将pv封装到该属性对象内部的propertyValueList集合中。
/**
 * BeanDefinitionParserDelegate的属性
 * name属性名常量
 */
public static final String NAME_ATTRIBUTE = "name";


/**
 1. BeanDefinitionParserDelegate的方法
 2. 

3. 解析一个标签元素 4. 5. @param ele 标签元素 6. @param bd 当前标签元素对应的BeanDefinition */ public void parsePropertyElement(Element ele, BeanDefinition bd) { //获取name属性值propertyName,即属性名称 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); //如果属性名称为null或者"",那么抛出异常 if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } //记录解析 this.parseState.push(new PropertyEntry(propertyName)); try { //如果当前标签元素对应的BeanDefinition的propertyValues属性中已经包含了当前属性名称,那么抛出异常 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } /* * 核心代码。解析子标签元素的值,可能是value属性、ref属性、或者子标签,返回相应的值封装对象 * * 这个方法和解析constructor-arg标签的代码中调用的是同一个方法,区别是propertyName属性不为null * propertyName属性仅仅是为了异常日志打印而已,没有其他作用 */ Object val = parsePropertyValue(ele, bd, propertyName); //将属性名和解析出来的值封装成为一个PropertyValue对象pv PropertyValue pv = new PropertyValue(propertyName, val); //解析meta节点,很少使用 parseMetaElements(ele, pv); //设置源 pv.setSource(extractSource(ele)); //将当前子标签的解析结果pv封装到BeanDefinition中的propertyValues属性中 bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }

1.1.1.2.5 parsePropertyValue解析标签元素的值

  parsePropertyValue方法用于解析、获取< property/>标签和< constructor-arg/>标签的值,这里的值可能来自于value属性、ref属性,或者是一个< list/>、< map/>等等子标签,因此返回的值可能是标签上的一个value属性、ref属性的解析结果,也可能是标签内部的值标签,比如list、map等标签的解析结果。
  在解析< constructor-arg/>标签的值时,"propertyName"属性为 null,在解析< property/>标签的值时,"propertyName"属性为不为null。这个propertyName属性主要用于异常日志输出,没啥其他用处。
  大概步骤为:

  1. 校验“值子标签”只能有一个,对于< property/>、< constructor-arg/>标签内部不属于< description/>和< meta/>标签的其他子标签,比如< ref/>, < value/>, < list/>,< etc/>,< map/>等等,它们都被算作"值子标签"。值子标签就是对应着< property/>、< constructor-arg/>标签的值,Spring要求最多只能有其中一个值子标签,校验不通过则抛出异常“must not contain more than one sub-element”。
  2. 继续校验< property/>标签和< constructor-arg/>标签的ref属性、value属性以及值子标签这三者只能有一个,因为这三个都表示该< property/>标签和< constructor-arg/>的值,只需要一个就行了,校验不通过则抛出异常“is only allowed to contain either ‘ref’ attribute OR ‘value’ attribute OR sub-element”。
  3. 校验通过,开始解析:
    1. 如果具有ref属性,那么肯定没有value属性以及值子标签,那么解析ref属性:
      1. 获取ref属性值refName,如果refName为””,那么抛出异常。
      2. 根据refName新建一个RuntimeBeanReference对象,这就是一个值对象,表示引用了其他对象,将在创建该bean实例时动态的解析,返回该对象,方法结束!
    2. 如果具有value属性,那么肯定ref属性以及值子标签,那么解析value属性:
      1. 根据value属性的值,创建一个TypedStringValue对象,这就是一个值对象,表示一个String类型的值对象,仅仅持有value字符串的,运行时值可以转换为其它类型,比如int、long等等,返回该对象,方法结束!
    3. 否则,如果具有值子标签,那么肯定没有ref属性以及value属性,那么调用parsePropertySubElement方法统一解析所有类型的值子标签,并返回解析结果。
    4. 否则,啥都没有设置,抛出异常。
//--------BeanDefinitionParserDelegate的相关属性---------

public static final String META_ELEMENT = "meta";

public static final String DESCRIPTION_ELEMENT = "description";

public static final String REF_ATTRIBUTE = "ref";

public static final String VALUE_ATTRIBUTE = "value";

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 用于解析、获取标签元素的值 * 这里的值可能来自value属性、ref属性,或者是一个等等子标签 *

* 在解析标签的值时,"propertyName"属性为 null * 在解析标签的值时,"propertyName"属性为不为null * 这个propertyName属性主要用于异常日志输出,没啥其他用处。 * * @param ele 对应标签元素 * @param bd 当前标签元素对应的BeanDefinition * @param propertyName 属性名 */ @Nullable public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null ? " element for property '" + propertyName + "'" : " element"); /* * 1 校验 值子标签只能有一个 * * 对于标签内部不属于标签的其他子标签 * 比如, , , ,等等,它们都被算作"值子标签" * 值子标签就是对应着标签的值,Spring要求最多只能有其中一个值子标签 */ //获取当前标签结点元素的子节点元素 NodeList nl = ele.getChildNodes(); //临时变量subElement记录找到的子节点标签 Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果node属于标签,并且不属于标签,比如, , , ,等等 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. //如果subElement不为null,说明在循环中找到了多个值子标签,那么直接抛出异常 if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } //否则,subElement记录找到的子节点标签 else { subElement = (Element) node; } } } /* * 2 到这里,表示校验通过,可能有一个值子标签,也可能一个都没有 * * 下面继续校验:ref属性、value属性、值子标签这三者只能有一个 */ //是否具有ref属性 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); //是否具有value属性 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); //如果同时具有ref属性、value属性、子标签这三者中的多个 if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { //那么抛出异常,property标签和constructor-arg标签只能拥有ref属性、value属性、子标签这三者中的一个 error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } /*3 校验通过,开始解析*/ /*3.1如果具有ref属性,那么肯定没有value属性以及值子标签*/ if (hasRefAttribute) { //获取ref属性值refName String refName = ele.getAttribute(REF_ATTRIBUTE); //如果refName为空,那么抛出异常 if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } //创建一个运行时bean引用对象,持有refName,相当于占位符,在后面运行时将会被解析为refName对应的bean实例的引用 RuntimeBeanReference ref = new RuntimeBeanReference(refName); //设置源,运行时将根据源解析 ref.setSource(extractSource(ele)); //返回这个对象 return ref; } /*3.2 否则,如果具有value属性,那么肯定没有其他二者*/ else if (hasValueAttribute) { //创建一个TypedStringValue对象,仅仅持有value字符串的,运行时值可以转换为其它类型,比如int、long等等 //不过,转换的这个功能是由conversion转换服务提供的 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); //设置源,运行时将根据源解析 valueHolder.setSource(extractSource(ele)); //返回这个对象 return valueHolder; } /*3.3 否则,如果具有值子标签,那么肯定没有ref属性以及value属性*/ else if (subElement != null) { /*继续解析值子标签*/ return parsePropertySubElement(subElement, bd); } /*3.4 否则,啥都没有,抛出异常*/ else { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); //返回null return null; } }

1.1.1.2.5.1 parsePropertySubElement解析值子标签

  < property/>或者< constructor-arg/>标签元素的值也是可以使用子标签表示的,可能是< bean/>、< ref/>、< idref/>……等标签,此时调用parsePropertySubElement方法,用于解析< property/>或者< constructor-arg/>标签内部对应的值子标签。
  该方法可以解析的值子标签类型有:< bean/>、< ref/>、< idref/>、< value/>、< null/>、< array/>、< list/>、< set/>、< map/>、< props/>等标签。

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 该方法仅仅被parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName)方法调用 * 解析标签下面的一个……等值子标签 * * @param ele 标签的一个值子标签元素,不知道具体是什么类型的 * @param bd 当前所属标签元素对应的BeanDefinition */ @Nullable public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) { //调用另一个parsePropertySubElement方法,第三个属性为null return parsePropertySubElement(ele, bd, null); }

  下面的三个参数的parsePropertySubElement方法非常重要。
  该方法用于解析< property/>或者< constructor-arg/>标签下面的值子标签,同时用于解析单value集合标签(< array/>、< list/>、< set/>)下的值子标签,以及< map/>集合标签下的< key/>标签的子标签和表示value的值子标签,以及标签的递归解析。

//------BeanDefinitionParserDelegate的相关属性-------

public static final String BEAN_ELEMENT = "bean";

public static final String REF_ELEMENT = "ref";

public static final String BEAN_REF_ATTRIBUTE = "bean";

public static final String PARENT_REF_ATTRIBUTE = "parent";

public static final String IDREF_ELEMENT = "idref";

public static final String VALUE_ELEMENT = "value";

public static final String NULL_ELEMENT = "null";

public static final String ARRAY_ELEMENT = "array";

public static final String LIST_ELEMENT = "list";

public static final String SET_ELEMENT = "set";

public static final String MAP_ELEMENT = "map";

public static final String PROPS_ELEMENT = "props";

/**
 * BeanDefinitionParserDelegate的方法,该方法也被其他子标签的解析方法调用
 * 

* 该方法用于解析或者标签下面的值子标签 * 同时用于解析单value集合标签()下的值子标签 * 以及集合标签下的标签的子标签和表示value的值子标签 * * @param ele 或者标签的一个值子标签元素,不知道具体是什么类型的 * @param bd 当前所属标签元素对应的BeanDefinition * @param defaultValueType 标签的默认value-type属性,这里的标签是指作为集合相关标签的子标签, * 它将会使用它的父集合标签的value-type或者key-type属性值作为自己的属性 * 而不是property或者constructor-arg标签下面直接设置的子标签 */ @Nullable public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { /* * 1 如果不是默认命名空间下的标签,那么走自定义的解析逻辑 */ if (!isDefaultNamespace(ele)) { //会返回一个BeanDefinitionHolder作为值 return parseNestedCustomElement(ele, bd); } /* * 2 否则,如果值子标签是标签,这表示内部bean */ else if (nodeNameEquals(ele, BEAN_ELEMENT)) { //那么递归调用parseBeanDefinitionElement解析内部bean,这里的db就不为null,使用的就是外部包含bean的BeanDefinition BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { //对于子标签下的自定义属性和子标签进行解析 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } //返回BeanDefinitionHolder对象作为解析后的值 return nestedBd; } /* * 3 否则,如果子标签是标签,这表示引用其他bean */ else if (nodeNameEquals(ele, REF_ELEMENT)) { // 获取bean属性的值,即引用的其他bean的名字refName String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; //如果当前标签的refName为null或者"" if (!StringUtils.hasLength(refName)) { //那么获取parent属性的值,将其作为引用的其他bean的名字refName refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; //如果还是空的,那么抛出异常 if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for element", ele); return null; } } //如果还是空的或者不是文本,那么抛出异常 if (!StringUtils.hasText(refName)) { error(" element contains empty target attribute", ele); return null; } //同样使用RuntimeBeanReference包装该属性值作为解析后的值,将会在运行时转换为对其他bean的引用 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } /* * 4 否则,如果子标签是标签,这表示引用字符串并校验bean */ else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } /* * 5 否则,如果子标签是标签,这表示引用字面量值 */ else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } /* * 6 否则,如果子标签是标签,这表示使用null作为值 */ else if (nodeNameEquals(ele, NULL_ELEMENT)) { //包装成为TypedStringValue作为解析后的值返回 TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } /* * 7 否则,如果子标签是标签,这表示值是一个数组 */ else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } /* * 8 否则,如果子标签是标签,这表示值是一个list集合 */ else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } /* * 9 否则,如果子标签是标签,这表示值是一个set集合 */ else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } /* * 10 否则,如果子标签是标签,这表示值是一个map集合 */ else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } /* * 11 否则,如果子标签是标签,这表示值是一个properties集合 */ else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } /* * 12 否则,没找到任何值子标签,抛出异常 */ else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }

1.1.1.2.5.1.1 parseValueElement解析< value/>值标签

  parseValueElement用于解析< value/>值标签,将结果封装成为一个TypedStringValue对象返回,内部还可以包含指定的类型,在运行时会进行转换。

/**
 * BeanDefinitionParserDelegate的属性
 * type属性名常量
 */
public static final String TYPE_ATTRIBUTE = "type";

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 返回给定值标签元素的解析的TypedStringValue对象 * * @param ele 值标签元素 * @param defaultTypeName 外部标签指定的value-type或者key-type */ public Object parseValueElement(Element ele, @Nullable String defaultTypeName) { //获取字面值 String value = DomUtils.getTextValue(ele); //获取type属性值specifiedTypeName String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE); String typeName = specifiedTypeName; //如果没有指定类型,那么使用外部标签指定的type if (!StringUtils.hasText(typeName)) { typeName = defaultTypeName; } try { //使用typeName和value创建TypedStringValue对象typedValue返回 TypedStringValue typedValue = buildTypedStringValue(value, typeName); typedValue.setSource(extractSource(ele)); typedValue.setSpecifiedTypeName(specifiedTypeName); return typedValue; } catch (ClassNotFoundException ex) { error("Type class [" + typeName + "] not found for element", ele, ex); return value; } } /** * BeanDefinitionParserDelegate的方法 *

* 使用给定的值和目标类型,构建一个TypedStringValue * TypedStringValue用于包装值标签元素的解析结果,内部还可以指定需要转换的类型 */ protected TypedStringValue buildTypedStringValue(String value, @Nullable String targetTypeName) throws ClassNotFoundException { ClassLoader classLoader = this.readerContext.getBeanClassLoader(); TypedStringValue typedValue; //如果没有指定类型targetTypeName,那么使用默认那么使用要给value的参数构建一个TypedStringValue if (!StringUtils.hasText(targetTypeName)) { typedValue = new TypedStringValue(value); } //否则,如果类加载器不为null else if (classLoader != null) { //那么获取targetTypeName类型的Class对象,并构建一个TypedStringValue Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader); typedValue = new TypedStringValue(value, targetType); } //否则,使用两个字符串参数构建一个 else { typedValue = new TypedStringValue(value, targetTypeName); } return typedValue; }

1.1.1.2.5.1.2 解析< array/>、< list/>、< set/>值标签

  < array/>、< list/>、< set/>值标签的特点就是单value类型的集合,它们的解析方法比较相近,这里统一讲解。对应的解析结果分别是ManagedArray、ManagedList、ManagedSet类型的对象。
  此前IoC学习的时候我们讲过Spring支持集合合并,这里也会调用parseMergeAttribute方法处理merge属性。
  对于集合标签内部的子标签,可能是其他任何值标签,因此需要同样递归上面三个参数的parsePropertySubElement方法进行递归解析。

/**
 * BeanDefinitionParserDelegate的属性
 * value-type属性名常量
 */
public static final String VALUE_TYPE_ATTRIBUTE = "value-type";


/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析标签元素 */ public Object parseArrayElement(Element arrayEle, @Nullable BeanDefinition bd) { //获取value-type属性值elementType String elementType = arrayEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //获取标签的子标签集合,里面的一个子标签相当于一个数组元素 NodeList nl = arrayEle.getChildNodes(); //创建用于托管数组元素的标记集合类ManagedArray,其中可能包括运行时 bean 引用(要解析为 bean 对象)。 //将会返回该对象作为解析结果 ManagedArray target = new ManagedArray(elementType, nl.getLength()); target.setSource(extractSource(arrayEle)); //设置elementTypeName属性 target.setElementTypeName(elementType); //设置mergeEnabled属性,表示是否需要合并父bean的集合属性 target.setMergeEnabled(parseMergeAttribute(arrayEle)); //调用parseCollectionElements继续解析 parseCollectionElements(nl, target, bd, elementType); return target; } /** * BeanDefinitionParserDelegate的方法 *

* 解析标签元素 */ public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) { //获取value-type属性值elementType String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //获取标签的子标签集合,里面的一个子标签相当于一个list集合元素 NodeList nl = collectionEle.getChildNodes(); //创建用于托管List元素的标记集合类ManagedList,其中可能包括运行时 bean 引用(要解析为 bean 对象)。 //将会返回该对象作为解析结果 ManagedList<Object> target = new ManagedList<>(nl.getLength()); target.setSource(extractSource(collectionEle)); target.setElementTypeName(defaultElementType); //设置mergeEnabled属性,表示是否需要合并父bean的集合属性 target.setMergeEnabled(parseMergeAttribute(collectionEle)); //调用parseCollectionElements继续解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } /** * BeanDefinitionParserDelegate的方法 *

* 解析标签元素 */ public Set<Object> parseSetElement(Element collectionEle, @Nullable BeanDefinition bd) { //获取value-type属性值elementType String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //获取标签的子标签集合,里面的一个子标签相当于一个set集合元素 NodeList nl = collectionEle.getChildNodes(); //创建用于托管Set元素的标记集合类ManagedSet,其中可能包括运行时 bean 引用(要解析为 bean 对象) //将会返回该对象作为解析结果 ManagedSet<Object> target = new ManagedSet<>(nl.getLength()); target.setSource(extractSource(collectionEle)); target.setElementTypeName(defaultElementType); //设置mergeEnabled属性,表示是否需要合并父bean的集合属性 target.setMergeEnabled(parseMergeAttribute(collectionEle)); //调用parseCollectionElements继续解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } //------------相关共用方法和属性------------------ public static final String MERGE_ATTRIBUTE = "merge"; public static final String TRUE_VALUE = "true"; /** * BeanDefinitionParserDelegate的方法 *

* 处理集合标签的merge属性,用于与父bean配置的集合合并。 */ public boolean parseMergeAttribute(Element collectionElement) { //获取merger属性 String value = collectionElement.getAttribute(MERGE_ATTRIBUTE); //如果是默认值,那么获取外部的default-merge属性或者自己继承的属性 if (isDefaultValue(value)) { value = this.defaults.getMerge(); } //如果value等于"true"字符串,那么返回true,表示合并;否则就是false,不合并 return TRUE_VALUE.equals(value); } public static final String DESCRIPTION_ELEMENT = "description"; /** 1. BeanDefinitionParserDelegate的方法 2.

3. 解析单value类型的集合标签 4. 5. @param elementNodes 单value类型的集合标签内部子标签集合 6. @param target 用于托管集合元素的标记集合类 7. @param bd bean定义 8. @param defaultElementType value-type属性值 */ protected void parseCollectionElements( NodeList elementNodes, Collection<Object> target, @Nullable BeanDefinition bd, String defaultElementType) { //循环子标签集合 for (int i = 0; i < elementNodes.getLength(); i++) { Node node = elementNodes.item(i); //如果属于标签并且不是description标签 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) { //那么递归调用parsePropertySubElement解析这些标签,将返回的结果添加到target中 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); } } }

1.1.1.2.5.1.3 parseMapElement解析< map/>值标签

  < map/>集合标签的解析和上面的单value集合标签的解析有些区别,它更加复杂,将会返回ManagedMap类型的解析结果。大概步骤如下:

  1. 获取相关属性:key-type、value-type、merge,将值设置到用于托管Map元素的标记集合类ManagedMap对象中。
  2. 遍历< map/>集合标签下的遍历所有子标签元素,进行解析:
    1. 遍历< entry/>子标签的子标签集合,尝试校验并获取key和value对应的标签,可能为null。key对应的< key/>子标签可以没有,但最多有一个;value对应的value值子标签可以没有,但最多有一个;
    2. 解析key的值。这里就类似于parsePropertyValue方法的逻辑,key的值可能来自于< entry/>标签的key属性、key-ref属性、或者< key/>标签。在同一个< entry/>标签中只能出现三者的其中一个。
    3. 解析value的值。这里就类似于parsePropertyValue方法的逻辑,value的值可能来自于< entry/>标签的value属性、value-ref属性或者其他value值标签。在同一个< entry/>标签中只能出现三者的其中一个。
      1. 如果设置了value-type属性,那么只能使用value属性设置value值,但是如果使用value属性,那么可以不设置value-type属性。
    4. 将解析之后的key、value值添加到ManagedMap中。
  3. 返回ManagedMap对象。

  从源码就能看到对于其他的值子标签的解析同样是递归调用三个参数的parsePropertySubElement方法解析,这里的第三个参数defaultValueType就是对应着外层标签的key-type,value-type属性,或者< entry/>标签的value-type属性(将覆盖< map/>标签的value-type属性)。

//------BeanDefinitionParserDelegate的相关属性------

public static final String VALUE_TYPE_ATTRIBUTE = "value-type";

public static final String KEY_TYPE_ATTRIBUTE = "key-type";

public static final String ENTRY_ELEMENT = "entry";

public static final String KEY_ELEMENT = "key";

public static final String DESCRIPTION_ELEMENT = "description";

public static final String KEY_ATTRIBUTE = "key";

public static final String KEY_REF_ATTRIBUTE = "key-ref";

public static final String VALUE_REF_ATTRIBUTE = "value-ref";

public static final String VALUE_ATTRIBUTE = "value";

/**
 * BeanDefinitionParserDelegate的方法
 * 

* 解析标签元素 * * @param bd BeanDefinition * @param mapEle 标签元素 */ public Map<Object, Object> parseMapElement(Element mapEle, @Nullable BeanDefinition bd) { /* * 1 获取、设置相关属性:key-type、value-type、merge,设置到用于托管Map元素的标记集合类ManagedMap中 */ //获取key-type属性值defaultKeyType作为key的默认类型 String defaultKeyType = mapEle.getAttribute(KEY_TYPE_ATTRIBUTE); //获取value-type属性值defaultValueType作为value的默认类型 String defaultValueType = mapEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //获取所有的子标签,一个entry表示一个键值对 List<Element> entryEles = DomUtils.getChildElementsByTagName(mapEle, ENTRY_ELEMENT); //创建用于托管Map元素的标记集合类ManagedMap,其中可能包括运行时 bean 引用(要解析为 bean 对象)。 ManagedMap<Object, Object> map = new ManagedMap<>(entryEles.size()); //设置相关属性 map.setSource(extractSource(mapEle)); map.setKeyTypeName(defaultKeyType); map.setValueTypeName(defaultValueType); //设置mergeEnabled属性,表示是否需要合并父bean的集合属性 map.setMergeEnabled(parseMergeAttribute(mapEle)); /* * 2 遍历所有内部的子标签元素,进行解析 */ for (Element entryEle : entryEles) { //子标签内部只能有一个值标签作为键值对的value,只能有一个标签作为键值对的key NodeList entrySubNodes = entryEle.getChildNodes(); //记录标签,同时用于校验重复 Element keyEle = null; //记录value值标签,同时用于校验重复 Element valueEle = null; /* * 2.1 遍历子标签的子标签,尝试校验并获取key和value对应的标签 * * key对应的子标签可以没有,但最多有一个 * value对应的value值子标签可以没有,但最多有一个 */ for (int j = 0; j < entrySubNodes.getLength(); j++) { Node node = entrySubNodes.item(j); if (node instanceof Element) { //标签的子标签 Element candidateEle = (Element) node; //如果是标签 if (nodeNameEquals(candidateEle, KEY_ELEMENT)) { //如果keyEle不为null,那么说明当前标签的子标签出现多个标签,抛出异常 if (keyEle != null) { error(" element is only allowed to contain one sub-element", entryEle); } else { //否则记录 keyEle = candidateEle; } } //如果不是标签 else { //如果是标签,那么丢弃 // Child element is what we're looking for. if (nodeNameEquals(candidateEle, DESCRIPTION_ELEMENT)) { // the element is a -> ignore it } //否则,就是表示value的值标签 // 如果valueEle不为null,那么说明当前标签的子标签出现多个值标签,抛出异常 else if (valueEle != null) { error(" element must not contain more than one value sub-element", entryEle); } else { //否则记录 valueEle = candidateEle; } } } } /* * 2.2 解析key的值 * 这里就类似于parsePropertyValue方法的逻辑,key的值可能来自于标签的key属性、key-ref属性、或者标签 * 在同一个标签中只能出现三者的其中一个 */ Object key = null; //是否设置了key属性 boolean hasKeyAttribute = entryEle.hasAttribute(KEY_ATTRIBUTE); //是否设置了key-ref属性 boolean hasKeyRefAttribute = entryEle.hasAttribute(KEY_REF_ATTRIBUTE); //如果key属性、key-ref属性、标签,这三者同时拥有两个及其之上的元素,那么抛出异常 if ((hasKeyAttribute && hasKeyRefAttribute) || (hasKeyAttribute || hasKeyRefAttribute) && keyEle != null) { error(" element is only allowed to contain either " + "a 'key' attribute OR a 'key-ref' attribute OR a sub-element", entryEle); } /*2.2.1 如果具有key属性,那么肯定没有key-ref属性以及子标签*/ if (hasKeyAttribute) { //key封装为一个TypedStringValue对象 key = buildTypedStringValueForMap(entryEle.getAttribute(KEY_ATTRIBUTE), defaultKeyType, entryEle); } /*2.2.2 如果具有key-ref属性,那么肯定没有key属性以及子标签*/ else if (hasKeyRefAttribute) { String refName = entryEle.getAttribute(KEY_REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(" element contains empty 'key-ref' attribute", entryEle); } //key封装为一个RuntimeBeanReference对象 RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(entryEle)); key = ref; } /*2.2.3 否则,如果具有子标签,那么肯定没有key属性以及key-ref属性*/ else if (keyEle != null) { //调用parseKeyElement解析子标签 key = parseKeyElement(keyEle, bd, defaultKeyType); } /*2.2.4 否则,啥都没有,抛出异常*/ else { error(" element must specify a key", entryEle); } /* * 2.3 解析value的值 * 这里就类似于parsePropertyValue方法的逻辑,value的值可能来自于标签的value属性、value-ref属性或者其他value值标签 * 在同一个标签中只能出现三者的其中一个 */ // Extract value from attribute or sub-element. Object value = null; //是否设置了value属性 boolean hasValueAttribute = entryEle.hasAttribute(VALUE_ATTRIBUTE); //是否设置了value-ref属性 boolean hasValueRefAttribute = entryEle.hasAttribute(VALUE_REF_ATTRIBUTE); //是否设置了value-type属性 boolean hasValueTypeAttribute = entryEle.hasAttribute(VALUE_TYPE_ATTRIBUTE); //如果value属性、value-ref属性、value值标签,这三者同时拥有两个及其之上的元素,那么抛出异常 if ((hasValueAttribute && hasValueRefAttribute) || (hasValueAttribute || hasValueRefAttribute) && valueEle != null) { error(" element is only allowed to contain either " + "'value' attribute OR 'value-ref' attribute OR sub-element", entryEle); } //如果具有value-type属性,并且(具有value-ref属性,或者没有value属性,或者具有value值子标签,那么抛出异常 //意思就是value-type属性如果被设置了,那么一定和value属性绑定;反过来,如果设置了value属性,那么value-type属性则不一定需要 if ((hasValueTypeAttribute && hasValueRefAttribute) || (hasValueTypeAttribute && !hasValueAttribute) || (hasValueTypeAttribute && valueEle != null)) { error(" element is only allowed to contain a 'value-type' " + "attribute when it has a 'value' attribute", entryEle); } /*2.3.1 如果具有value属性,那么肯定没有value-ref属性以及value值子标签*/ if (hasValueAttribute) { //获取的value-type属性 String valueType = entryEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //如果没有设置value-type属性,那么使用外层标签指定的value-type属性 if (!StringUtils.hasText(valueType)) { valueType = defaultValueType; } //value封装为一个TypedStringValue对象 value = buildTypedStringValueForMap(entryEle.getAttribute(VALUE_ATTRIBUTE), valueType, entryEle); } /*2.3.2 如果具有key-ref属性,那么肯定没有value属性以及value值子标签*/ else if (hasValueRefAttribute) { String refName = entryEle.getAttribute(VALUE_REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(" element contains empty 'value-ref' attribute", entryEle); } //value封装为一个RuntimeBeanReference对象 RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(entryEle)); value = ref; } /*2.3.3 否则,如果具有value值子标签,那么肯定没有value属性以及value-ref属性*/ else if (valueEle != null) { //递归调用parsePropertySubElement方法解析value值子标签 value = parsePropertySubElement(valueEle, bd, defaultValueType); } /*2.3.4 否则,啥都没有,抛出异常*/ else { error(" element must specify a value", entryEle); } //解析之后的key、value值添加到ManagedMap中 // Add final key and value to the Map. map.put(key, value); } //返回ManagedMap对象 return map; } //----解析标签下的标签---- /** * 解析标签下的标签 * * @param keyEle 标签下的标签 * @param bd BeanDefinition * @param defaultKeyTypeName 外层标签指定的key-type属性 */ @Nullable protected Object parseKeyElement(Element keyEle, @Nullable BeanDefinition bd, String defaultKeyTypeName) { //获取标签的子标签集合 NodeList nl = keyEle.getChildNodes(); //记录标签的子标签,同时用于校验重复 Element subElement = null; //遍历集合 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { // 同样标签的子标签只能有一个 if (subElement != null) { error(" element must not contain more than one value sub-element", keyEle); } else { subElement = (Element) node; } } } //如果标签的子标签为null,那么返回null if (subElement == null) { return null; } //递归调用三个参数的parsePropertySubElement方法解析标签的子标签,使用外层标签指定的key-type属性 return parsePropertySubElement(subElement, bd, defaultKeyTypeName); }

1.1.1.2.5.1.4 解析< props/>集合值标签

  parsePropsElement方法用于解析< props/>集合值标签,比较简单,因为properties集合的key和value只能是String类型,不需要递归解析。将会返回ManagedProperties类型的解析结果。

//------BeanDefinitionParserDelegate的相关属性------

public static final String PROP_ELEMENT = "prop";

public static final String KEY_ATTRIBUTE = "key";

/**
 1. BeanDefinitionParserDelegate的方法
 2. 

3. 解析标签元素 4. 比较简单,因为properties集合的key和value只能是String类型,不需要递归解析 5. 6. @param propsEle 标签元素 */ public Properties parsePropsElement(Element propsEle) { //创建用于托管Properties元素的标记集合类ManagedProperties ManagedProperties props = new ManagedProperties(); props.setSource(extractSource(propsEle)); //设置mergeEnabled属性,表示是否需要合并父bean的集合属性 props.setMergeEnabled(parseMergeAttribute(propsEle)); //获取所有的子标签集合,一个prop表示一个键值对 List<Element> propEles = DomUtils.getChildElementsByTagName(propsEle, PROP_ELEMENT); /* * 遍历子标签,解析 */ for (Element propEle : propEles) { //获取key属性的值 String key = propEle.getAttribute(KEY_ATTRIBUTE); // Trim the text value to avoid unwanted whitespace // caused by typical XML formatting. //获取子标签的值,就是value值,去除前后空格 String value = DomUtils.getTextValue(propEle).trim(); //使用TypedStringValue封装key和value的值 TypedStringValue keyHolder = new TypedStringValue(key); keyHolder.setSource(extractSource(propEle)); TypedStringValue valueHolder = new TypedStringValue(value); valueHolder.setSource(extractSource(propEle)); //解析之后的key、value值添加到ManagedProperties中 props.put(keyHolder, valueHolder); } //返回ManagedProperties return props; }

1.1.2 registerBeanDefinition注册Bean定义

  当我们将BeanDefinitionHolder创建完毕之后,所谓的bean定义就创建完了,随后会调用BeanDefinitionReaderUtils.registerBeanDefinition方法将BeanDefinitionHolder注册到注册表Registry中,这个Registry实际上就是上下文容器内部的DefaultListableBeanFactory实例。
  如果该方法注册完毕并且发送注册事件完毕之后,那么refresh()方法中的obtainFreshBeanFactory方法步骤就算真正结束了,随后将会进入下一步。

  registerBeanDefinition方法的大概逻辑就是:

  1. 获取BeanDefinitionHolder对象中持有的BeanDefinition、beanName和aliases。
  2. 调用registerBeanDefinition方法。将beanName和对应的BeanDefinition注册到registry的对应缓存集合中,这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory实例中的beanDefinitionMap和beanDefinitionNames两个缓存。
  3. 调用registerAlias方法,将beanName和aliases中的每一个别名注册到registry的缓存中。这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory的父类SimpleAliasRegistry注册表实例中的aliasMap缓存。

  这些缓存并不是对象实例的缓存,实际上到目前为止还没有bean被初始化,但是很明显,这些缓存定义了一个bean实例的全部特点,是为后面的bean实例初始化做准备的。

/**
 * BeanDefinitionReaderUtils的方法
 * 

* 向给定的 bean 工厂注册给定的 bean 的定义 * * @param definitionHolder 封装了了bean 定义、包括bean的名称和别名 * @param registry bean工厂的注册器 * 这个Registry实际上就是容器内部的DefaultListableBeanFactory实例, * 因为DefaultListableBeanFactory实现了BeanDefinitionRegistry接口 */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //获取bean的名称 String beanName = definitionHolder.getBeanName(); /* * 1 将beanName和对应的BeanDefinition注册到registry中的相关缓存中 * 这个Registry实际上就是容器内部的DefaultListableBeanFactory实例 */ registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); /* * 2 注册 bean的别名(如果有) */ String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { //注册bean的名称和对应的每一个别名的映射到缓存SimpleAliasRegistry的aliasMap缓存集合中 registry.registerAlias(beanName, alias); } } }

1.1.2.1 registerBeanDefinition注册BeanDefinition

  registerBeanDefinition方法大概步骤为:
  将beanName和BeanDefinition注册到registry的缓存中。这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory实例中的beanDefinitionMap和beanDefinitionNames两个缓存。同时还会使用到allowBeanDefinitionOverriding属性判断是否可以进行bean定义的覆盖。

  registerBeanDefinition方法详细步骤为:

  1. 首先尝试从注册表的缓存beanDefinitionMap中查找当前beanName的bean定义existingDefinition。
  2. 如果existingDefinition不为null,即找到了同名bean定义,那么进行ban定义覆盖的校验和操作:
    1. 通过allowBeanDefinitionOverriding属性判断是否不允许 bean 的覆盖,如果不允许,那么抛出异常;如果允许,那么进行覆盖,即设置beanDefinitionMap缓存的该beanName的value值为最新的BeanDefinition。
  3. 如果existingDefinition为null,即没找到同名bean定义,那么将beanName以及对应的beanDefinition设置到beanDefinitionMap缓存,将当前的beanName存入beanDefinitionNames缓存,将当前的beanName从手动注册bean名称集合manualSingletonNames缓存中移除。
  4. 如果找到了同名bean定义,或者单例bean实例的缓存singletonObjects已中包含给定beanName的实例。那么将当前beanName对应的在DefaultSingletonBeanRegistry中的实例缓存清除,重置给定 beanName 的所有 bean 定义缓存,包括从它派生的 bean 的缓存(merge),以及重置以给定beanName为父类bean的子类Bean缓存。
    1. 否则如果此工厂的 bean 定义被冻结,即不应进一步修改或后处理,那么删除所有的按类型映射的任何缓存:allBeanNamesByType和singletonBeanNamesByType。在finishBeanFactoryInitialization方法中就会冻结 bean 定义,并且进行bean初始化操作。
//-----------DefaultListableBeanFactory的相关属性----------

/**
 * beanName到bean definition映射的map,这就是一个beanDefinition注册表缓存
 * 后面创建bean实例的时候会使用
 */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

/**
 * 按注册顺序排列的beanName的列表
 */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

/**
 * DefaultListableBeanFactory的方法
 * 

* 在此注册表缓存中注册一个新的 bean 定义 * 这里将会校验前面设置的allowBeanDefinitionOverriding属性 * * @param beanName 要注册的 bean 实例的名称 * @param beanDefinition 要注册的 bean 实例的定义 */ @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //bean definition注册前的校验,methodOverrides校验 if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //尝试从注册表缓存中查找当前beanName的BeanDefinition BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); /* * 1 如果找到了同名的BeanDefinition,进行ban定义覆盖的校验和操作 */ if (existingDefinition != null) { /* * 判断是否不允许 bean 的覆盖 * * allowBeanDefinitionOverriding属性我们在“customizeBeanFactory配置beanFactory”的部分已经讲过 * 普通应用默认为true,boot应用默认false,可以自定义配置 */ if (!isAllowBeanDefinitionOverriding()) { //如果不允许,那么就是出现同名bean,那么直接抛出异常 throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } /* * 否则,表示允许,继续判断角色相关,不必关心 * 打印日志,用框架定义的 Bean 覆盖用户自定义的 Bean */ else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } /* * 否则,表示允许,继续如果当前的beanDefinition不等于找到的此前的existingDefinition * 打印日志,将会使用新beanDefinition覆盖旧的beanDefinition */ else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } /* * 否则,表示允许,打印日志,将会使用同样(equals比较返回true)新的beanDefinition覆盖旧的beanDefinition */ else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //使用新的beanDefinition覆盖旧的existingDefinition this.beanDefinitionMap.put(beanName, beanDefinition); } /*2 如果没找到同名的BeanDefinition,这是正常情况*/ else { /*如果已经有其他任何bean实例开始初始化了*/ if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //加锁防止并发操作集合 synchronized (this.beanDefinitionMap) { //当前的beanName和beanDefinition存入beanDefinitionMap缓存 this.beanDefinitionMap.put(beanName, beanDefinition); //当前的beanName存入beanDefinitionNames缓存 //重新生成一个list集合替换 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; //当前的beanName从手动注册bean名称集合manualSingletonNames缓存中移除 //因为如果这里自动注册了beanName,那么需要从manualSingletonNames缓存中移除代表手动注册的单例beanName。 removeManualSingletonName(beanName); } } /*否则,其他任何bean实例没有开始初始化*/ else { //仍处于启动注册阶段,不加锁 //当前的beanName和beanDefinition存入beanDefinitionMap缓存 this.beanDefinitionMap.put(beanName, beanDefinition); //当前的beanName存入beanDefinitionNames缓存 this.beanDefinitionNames.add(beanName); //当前的beanName从手动注册bean名称集合manualSingletonNames缓存中移除 //因为如果这里自动注册了beanName,那么需要从manualSingletonNames缓存中移除代表手动注册的单例beanName。 removeManualSingletonName(beanName); } //仅仅在与初始化时才会使用到,很少使用 this.frozenBeanDefinitionNames = null; } /* * 3 如果找到的旧的BeanDefinition不为null,或者单例bean实例的缓存singletonObjects已中包含给定beanName的实例 * 那么将当前beanName对应的在DefaultSingletonBeanRegistry中的实例缓存清除,需要重新生成实例 */ if (existingDefinition != null || containsSingleton(beanName)) { /* * 将当前beanName对应的在DefaultSingletonBeanRegistry中的实例缓存清除 * 重置给定 beanName 的所有 bean 定义缓存,包括从它派生的 bean 的缓存(merge)。 * 以及重置以给定beanName为父类bean的子类Bean缓存。 */ resetBeanDefinition(beanName); } /* * 4 否则,如果此工厂的 bean 定义是否冻结,即不应进一步修改或后处理。 * 那么删除所有的按类型映射的任何缓存:allBeanNamesByType和singletonBeanNamesByType * 在finishBeanFactoryInitialization方法中就会冻结 bean 定义,并且进行bean初始化操作 * 一般不会出现 */ else if (isConfigurationFrozen()) { clearByTypeCache(); } }

1.1.2.1.1 resetBeanDefinition重置BeanDefinition缓存

  如果此前已经注册了同名的BeanDefinition,或者单例bean实例的缓存singletonObjects中已经包含给定beanName的实例,那么需要重置BeanDefinition的相关缓存。
  将DefaultSingletonBeanRegistry注册表中的当前beanName对应的实例缓存清除(destroySingleton),并且重置给定 beanName 的BeanDefinition缓存,包括已合并的 bean定义 缓存(mergedBeanDefinitions),以及重置以给定beanName为父类bean的子类Bean。

/**
 * DefaultSingletonBeanRegistry的属性
 * 单例的 beanName 到 bean instance的缓存
 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
 * 父类DefaultSingletonBeanRegistry的方法
 * 

* 判断单例bean实例的缓存中是否已经包含给定beanName的实例 * * @param beanName * @return */ @Override public boolean containsSingleton(String beanName) { return this.singletonObjects.containsKey(beanName); } /** * DefaultListableBeanFactory的方法 *

* 重置给定 bean 的所有 bean 定义缓存,包括从它派生的 bean 的缓存。 * 通知所有后处理器已重置指定的 bean 定义。 * * @param beanName 要重置的 bean 的名称 */ protected void resetBeanDefinition(String beanName) { //删除给定beanName的mergedBeanDefinitions的缓存,这是已合并的bean定义缓存 clearMergedBeanDefinition(beanName); //从单例缓存中删除相应的单例(如果有),这个方法之前讲过了 //实际上就是删除DefaultSingletonBeanRegistry中的关于单例bean实现的缓存 destroySingleton(beanName); //通知所有后处理器已重置指定的 bean 定义。 for (BeanPostProcessor processor : getBeanPostProcessors()) { if (processor instanceof MergedBeanDefinitionPostProcessor) { ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); } } // 重置所有以当前beanName为父类bean的子类Bean for (String bdName : this.beanDefinitionNames) { if (!beanName.equals(bdName)) { BeanDefinition bd = this.beanDefinitionMap.get(bdName); //如果 bd 不为null,并且给定beanName等于bd的parentName属性 if (bd != null && beanName.equals(bd.getParentName())) { //递归调用resetBeanDefinition重置BeanDefinition resetBeanDefinition(bdName); } } } }

1.1.2.2 registerAlias注册别名映射

  别名映射缓存aliasMap位于SimpleAliasRegistry注册表中,SimpleAliasRegistry是DefaultListableBeanFactory的父类。
  aliasMap是一个map,key为一个alias别名,value就是beeanName,通过名称获取bean实例的时候就会从别名映射缓存中先获取真实的beanName,然后再获取beanName对应的bean实例。

/**
 * SimpleAliasRegistry的属性
 * 别名映射的map缓存 alias -> beanName
 */
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

/**
 1. 父类SimpleAliasRegistry的方法
 2. 

3. 注册别名映射缓存 4. 5. @param name beanName 5. @param alias 一个别名 */ @Override public void registerAlias(String name, String alias) { //必须不为null也不能是空字符串 Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); //加锁 synchronized (this.aliasMap) { /*如果beanName就等于alias别名,那么不算做别名*/ if (alias.equals(name)) { //从缓存中移除指定alias为key的缓存 this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } /*否则,注册别名映射缓存*/ else { //尝试通过给定的别名从当aliasMap中获取以存在的beanName String registeredName = this.aliasMap.get(alias); //如果registeredName不为null,说明存在相同的别名 if (registeredName != null) { //如果registeredName等于name,说明已经注册过该beanName的别名了,直接返回,无需重新注册 if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } /* * 如果不允许别名覆盖,则抛出异常,默认是允许的 * 就是使用的前面讲的customizeBeanFactory方法设置的allowBeanDefinitionOverriding属性来判断 */ if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } //输出日志,别名映射缓存将会覆盖 if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } /* * 检查beanName和alias是否存在循环的name引用 * 比如a的别名是b,b的别名是a,那么抛出异常 */ checkForAliasCircle(name, alias); //没有循环的name引用,那么加入缓存 this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }

1.2 importBeanDefinitionResource解析< import/>标签注册bean定义

  Spring boot之前的web项目,为了将不同的配置分开方便管理,通常会有多个XML配置文件,然后再通过< import/>标签将其他配置文件引入到一个配置文件之中。
  过程比较简单,那就是根据resource属性指定的路径解析成Resource资源,然后调用loadBeanDefinitions加载资源中的bean定义即可,loadBeanDefinitions方法我们在此前解析< bean/>标签的processBeanDefinition方法中已经讲过了。
  注意点:

  1. 这里的路径既可以是绝对路径也可以是相对路径:
    1. 相对路径是根据当前< import/>标签所在文件路径将相对路径转为绝对路径然后再进行解析。
    2. 源码中,绝对路径还包括以"classpath*:"、"classpath:"开头的路径,因为"classpath:"开头的路径将会固定加载本项目resources目录下的配置文件,"classpath*:"开头的路径将会固定加载本项目resources目录下的配置文件以及引入的外部jar中配置文件。
  2. 这里的路径可以使用占位符:
    1. 默认是使用严格的模式解析占位符,没有默认值的无法解析的占位符则抛出IllegalArgumentException异常。占位符的属性一般都是我们单独设置在一个外部properties属性文件中的,然后通过< context:property-placeholder/>标签引入外部配置文件。
    2. 需要注意的是,< context:property-placeholder/>标签解析的属性并不会被注入到environment中,实际上是存放到PropertySourcesPlaceholderConfigurer对象中的,这个对象包括了environment环境变量的全部属性以及配置文件标签或者PropertySources加载进来的属性,实际上只会用来解析bean定义内的属性值和@Value、@Resource注解的值中的${…:…}占位符,而对于< import/>标签中以及其他标签的占位符的解析仅仅是从environment环境变量的属性集合中获取的,因此肯定由于获取外部配置不到而造成“Could not resolve placeholder的异常”。所以,我们可以在加载、解析标签之前将配置信息设置到environment环境变量中,比如System.setProperty和initPropertySources扩展点,对于web应用可以使用ApplicationContextInitializer配置容器启动加载的属性。关于properties属性文件的加载,后面会讲:Spring 5.x 源码(8)—PropertySourcesPlaceholderConfigurer占位符解析器源码深度解析。
/**
 * DefaultBeanDefinitionDocumentReader的属性
 */
public static final String RESOURCE_ATTRIBUTE = "resource";

/**
 * DefaultBeanDefinitionDocumentReader的方法
 * 

* 解析标签,将给定资源的 bean 定义加载到 bean 工厂。 */ protected void importBeanDefinitionResource(Element ele) { //获取resource属性值 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); //如果没有为null或者""或者空白字符,那么将抛出异常 if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } /* * 使用环境变量对象的resolveRequiredPlaceholders方法来解析资源路径字符串,这说明资源路径也可以使用占位符${..:..} * resolvePlaceholders方法将会使用严格模式,没有默认值的无法解析的占位符则抛出IllegalArgumentException异常 * 在setConfigLocations部分中我们就学过了该方法 */ location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<>(4); // 判断该路径是绝对的还是相对的 URI boolean absoluteLocation = false; try { //如果是以"classpath*:"、"classpath:"开头或者可以据此创立一个URL对象,或者此URI是一个绝对路径,那么就是绝对URI absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" } /*绝对路径*/ if (absoluteLocation) { try { //加载指定路径下面的配置文件 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isTraceEnabled()) { logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } /*相对路径*/ else { // No URL -> considering resource location as relative to the current file. try { int importCount; //尝试转换为单个资源 Resource relativeResource = getReaderContext().getResource().createRelative(location); //此资源是否存在确定的一个文件,如果使用了Ant通配符,表示匹配多个资源文件,那么不会存在 if (relativeResource.exists()) { //如果是确定的一个文件,那么解析该文件,这个loadBeanDefinitions方法在前面就讲过了 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { //获取基础文件路径,也就是标签所在文件路径 String baseLocation = getReaderContext().getResource().getURL().toString(); //根据当前标签所在文件路径将相对路径转为绝对路径然后再进行解析 importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isTraceEnabled()) { logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } //过去解析的所有Resource资源的数组 Resource[] actResArray = actualResources.toArray(new Resource[0]); //发布事件 getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }

1.3 processAliasRegistration解析< alias/>标签注册别名映射

  在XML中可以单独使用< alias/>标签来为bean定义设置别名。常见格式为:

<alias name="x" alias="y"/>

  name表示bean定义中的id或者name属性值,alias表示设置的别名。
  源码很简单,那就是获取name和alias属性,调用registerAlias方法,将beanName和alias注册到registry的缓存中。这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory的父类SimpleAliasRegistry注册表实例中的aliasMap缓存。
  我们在解析< bean/>标签的processBeanDefinition中就讲过了registerAlias方法,所以说,< bean/>标签中设置的别名和< alias/>标签设置的别名都是存放在同一个缓存aliasMap中的。再次提醒,一定不要出现循环别名映射,否则直接抛出异常,这个我们在前文也讲过了。

//--------DefaultBeanDefinitionDocumentReader的属性--------

public static final String NAME_ATTRIBUTE = "name";

public static final String ALIAS_ATTRIBUTE = "alias";

/**
 * DefaultBeanDefinitionDocumentReader的方法
 * 

*

* 解析给定的标签,在注册表中注册别名映射。 */ protected void processAliasRegistration(Element ele) { //获取name和alias属性 String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; //name不能为null、""、空白字符,否则抛出异常:"Name must not be empty" if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } //alias不能为null、""、空白字符,否则抛出异常:"Alias must not be empty" if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { /* * 调用registerAlias方法,将beanName和aliases中的每一个别名注册到registry的缓存中。 * 这个缓存实际上就是上下文容器内部的DefaultListableBeanFactory的父类SimpleAliasRegistry注册表实例中的aliasMap缓存。 * 这个方法我们在解析标签的processBeanDefinition中就讲过了,所以说 * 在标签中设置的别名和标签设置的别名都是存放在同一个缓存中的 */ getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } //发布事件 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }

2 parseCustomElement解析扩展标签

  在parseBeanDefinitions(root, this.delegate)方法中,对于基本标签也就是默认命名空间(http://www.springframework.org/schema/beans)下的标签的解析使用的是parseDefaultElement方法,比如< import/>、< alias/>、< bean/>、< beans/>。
  而对于我们引入的其他命名空间(比如http://www.springframework.org/schema/context、http://www.springframework.org/schema/aop)下的标签的解析则是采用的parseCustomElement方法,比如< mvc/>、< task/>、< context/>、< aop/>标签的解析。扩展标签的解析方法parseCustomElement直接位于BeanDefinitionParserDelegate解析器中。
  现在我们就来看看自定义命名空间下的标签的解析方法parseCustomElement的源码,这个方法也非常重要。比如annotation-config、component-scan等等注解支持的标签都是由它解析的。

  1. 首先获取readerContext中的namespaceHandlerResolver,在前面的createReaderContext方法中已被初始化了,随后通过namespaceHandlerResolver的resolve方法根据namespaceUri获取对应的handler对象,比如AopNamespaceHandler、ContextNamespaceHandler等等。
  2. 调用handler对象的parse方法解析标签节点元素,并传递一个新建的解析上下文ParserContext对象,同样是返回一个BeanDefinition类型的对象,但是有可能直接返回null,比如后面讲的< context:component-scan/>标签的解析,最终就直接返回null。
/**
 * DefaultBeanDefinitionDocumentReader的方法
 * 

* 解析自定义标签元素(在默认命名空间之外的标签) * * @param ele 要解析的标签元素 * @return 生成的bean定义 */ @Nullable public BeanDefinition parseCustomElement(Element ele) { //调用另一个parseCustomElement方法,containingBd参数为null return parseCustomElement(ele, null); } /** 1. DefaultBeanDefinitionDocumentReader的方法 2.

3. 解析自定义标签元素(在默认命名空间之外的标签) 4. 5. @param ele 要解析的标签元素 6. @param containingBd 外部包含bean的定义,可能为null 7. @return 生成的bean定义 */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { //获取节点的命名空间URI //节点的命名空间URI为 http://www.springframework.org/schema/context // 节点的命名空间URI为 http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } /* * 1 先获取readerContext中的namespaceHandlerResolver,在前面的createReaderContext方法中已被初始化了 * 随后通过namespaceHandlerResolver的resolve方法根据namespaceUri获取对应的handler对象 * 比如AopNamespaceHandler、ContextNamespaceHandler等等 */ NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); //如果没找到handler,那么返回并抛出异常 if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } /* * 2 使用handler的parse方法解析节点元素,传递一个新建的解析上下文ParserContext对象 */ return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }

2.1 resolve解析namespaceUri

  readerContext的namespaceHandlerResolver属性的resolve方法用于解析namespaceUri返回对应的NamespaceHandler。
  在前面的createReaderContext方法中就介绍过,namespaceHandlerResolver的默认实现就是DefaultNamespaceHandlerResolver。

  1. 首先会调用getHandlerMappings方法获取全部的处理器映射缓存集合handlerMappings:
    1. 这个缓存是在getHandlerMappings第一次调用时才会加载的(懒加载),将会加载本项目以及引入的外部jar包中的指定路径下的handler文件,默认位置就是"META-INF/spring.handlers"
    2. 加载进来的数据被存入该缓存map中,默认是namespaceUri字符串以及对应的NamespaceHandler的全路径类名字符串键值对。后续调用该方法时将不会加载而是直接从缓存获取
  2. 随后根据给定的namespaceUri作为key从该缓存中查找对应的value。如果是第一次查找给定的namespaceUri,那么获取的value仅仅是一个NamespaceHandler的全路径类名字符串:
    1. 因此在第一次调用到某个namespaceUri的resolve方法时,需要将value表示的全路径类名字符串反射创建为一个NamespaceHandler对象,并通过init()方法进行初始化。
    2. 随后使用该对象作为新的value替换handlerMappings缓存集合中原来的全路径类名字符串,方便后续直接从缓存中获取handler对象而不需要再次解析。
/**
 * DefaultNamespaceHandlerResolver的方法
 * 

* 懒加载handlerMappings,并且根据给定的namespaceUri解析NamespaceHandler * * @param namespaceUri 相关命名空间Uri * @return 加载的NamespaceHandler,或者null */ @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { /* * 1 获取全部handlerMappings集合 * handlerMappings在使用时才会被加载,懒加载,刚加载进来(第一次解析)时的样式为: * http://www.springframework.org/schema/p -> org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler * key和value都是字符串,key为namespaceUri字符串,value就是对应的NamespaceHandler的全路径类名字符串 */ Map<String, Object> handlerMappings = getHandlerMappings(); //然后根据给定的namespaceUri获取handlerOrClassName Object handlerOrClassName = handlerMappings.get(namespaceUri); /*如果没有获取到,直接返回null*/ if (handlerOrClassName == null) { return null; } /*否则,如果获取的value已经被解析为NamespaceHandler对象,那么直接返回*/ else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } /*否则,第一次解析handlerOrClassName*/ else { String className = (String) handlerOrClassName; try { //获取class对象 Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } //实例化NamespaceHandler对象 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); /* * 初始化NamespaceHandler对象,为当前命名空间下的不同标签节点指定不同的解析器 */ namespaceHandler.init(); //重新存入该namespaceUri,将handlerOrClassName,即全路径类名,替换为namespaceHandler,即解析后的对象,避免后续再次解析 handlerMappings.put(namespaceUri, namespaceHandler); //返回handler对象 return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } } }

2.1.1 getHandlerMappings获取NamespaceHandler映射

  getHandlerMappings方法用于获取处理器映射缓存,第一次调用该方法时将会从指定路径懒加载指定的mapping映射文件到handlerMappings缓存集合中,后续再次调用该方法时将直接返回缓存的map。

/**
 * DefaultNamespaceHandlerResolver的属性
 * 

* NamespaceHandler映射map集合 */ @Nullable private volatile Map<String, Object> handlerMappings; /** * DefaultNamespaceHandlerResolver的属性 *

* 要搜索的handlerMappings资源位置 * createReaderContext中创建DefaultNamespaceHandlerResolver的时候就被初始化为"META-INF/spring.handlers" */ private final String handlerMappingsLocation; /** * DefaultNamespaceHandlerResolver的方法 *

* 懒加载指定的NamespaceHandler映射 */ private Map<String, Object> getHandlerMappings() { //获取handlerMappings Map<String, Object> handlerMappings = this.handlerMappings; //如果为null,那么加载,不为null直接返回 if (handlerMappings == null) { //加锁 synchronized (this) { //再次判断 handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { //加载指定路径下的mapping文件为Properties集合,默认路径为"META-INF/spring.handlers" Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } //Properties转换为ConcurrentHashMap handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); //赋给handlerMappings this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } //返回handlerMappings return handlerMappings; }

2.1.2 init初始化标签节点解析/装饰器

  NamespaceHandler的init方法,用于为当前为命名空间下的不同标签节点指定不同的解析/装饰器,即BeanDefinitionParser和BeanDefinitionDecorator。
  比如Context命名空间下的ContextNamespaceHandler的init方法如下,我们可以看到常见的标签节点比如"component-scan",该标签的解析器就是一个ComponentScanBeanDefinitionParser实例。

/**
 * NamespaceHandler继承了NamespaceHandlerSupport,而NamespaceHandlerSupport实现了NamespaceHandler
 */
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
     

    /**
     * 为context 命名空间下的不同标签节点指定不同的解析器
     */
    @Override
    public void init() {
     
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

2.1.2.1 registerBeanDefinitionParser、registerBeanDefinitionDecorator注册bean定义解析/装饰器

  init()方法中的注册解析/装饰器的方法就是调用的registerBeanDefinitionParser/registerBeanDefinitionDecorator方法,这两个方法位于NamespaceHandlerSupport中,用于注册处理特定标签节点的BeanDefinitionParser和BeanDefinitionDecorator实现,注册的所有解析/装饰器保存在NamespaceHandlerSupport内部的parsers/decorators缓存map中。
  NamespaceHandlerSupport直接实现了NamespaceHandler,而其他的自定义的NamespaceHandler则是继承的NamespaceHandlerSupport。
  一个NamespaceHandler的具体实现仅仅是将解析某个命名空间所需要的全部解析器和装饰器统一调用registerBeanDefinitionParser和registerBeanDefinitionDecorator方法注册到NamespaceHandlerSupport中而已,比如ContextNamespaceHandler。
  NamespaceHandlerSupport是用于实现自定义NamespaceHandler的支持类,内部的parsers映射缓存了所有注册的解析器,decorators映射缓存了所有注册的装饰器,格式为:标签名字符串->解析器/装饰器实例。
  真正的的单个Node节点的解析和装饰则分别是通过BeanDefinitionParser解析器和BeanDefinitionDecorator装饰器这两个策略接口的实现来完成的。

//---------NamespaceHandlerSupport的相关属性-----------

/**
 * value存储BeanDefinitionParser的实现,对应的所处理的Element标签元素节点的本地名称作为key
 */
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();


/**
 * value存储BeanDefinitionDecorator的实现,对应的所处理的Element标签元素节点的本地名称作为key
 */
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();

/**
 * NamespaceHandlerSupport的方法
 * 

* 子类可以调用它来注册提供的BeanDefinitionParser,用来以处理指定的Element标签元素节点。元素名称是本地(非命名空间限定)名称。 */ protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); } /** * NamespaceHandlerSupport的方法 *

* 子类可以调用它来注册提供的BeanDefinitionDecorator来处理指定的元素。元素名称是本地(非命名空间限定)名称。 */ protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) { this.decorators.put(elementName, dec); }

2.2 parse解析自定义标签节点

  在parseCustomElement方法的最后是调用具体handler实现的parse方法来解析对应的标签节点元素。实际上是调用的父类NamespaceHandlerSupport实现的逻辑,而NamespaceHandlerSupport的parse方法同样是通过委派给此前通过init方法的注册到parsers缓存中的BeanDefinitionParser来解析对应的Element标签元素的。

/**
 * 具体handler实现的父类NamespaceHandlerSupport的方法
 * 

* 通过委派给的注册的BeanDefinitionParser来解析对应的Element标签元素 */ @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { //查找该element标签元素对应的BeanDefinitionParser解析器parser BeanDefinitionParser parser = findParserForElement(element, parserContext); //如果parser不为null,那么就使用解析器的parse方法来解析该标签,返回解析后的BeanDefinition return (parser != null ? parser.parse(element, parserContext) : null); } /** * NamespaceHandlerSupport的方法 *

* 使用提供的Element的本地名称从缓存parsers中查找BeanDefinitionParser */ @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { //获取element的本地名称 String localName = parserContext.getDelegate().getLocalName(element); //从parsers缓存中查找BeanDefinitionParser BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } //返回 return parser; }

3 小结

  这篇文章的内容是接着上一篇讲解obtainFreshBeanFactory方法的后半部分,我们对本次学习的重点做一个简单的总结,其中还有很多细节没有写出来,需要通看文章才能找到:

  1. parseDefaultElement解析默认标签的方法,用于解析< beans/>根标签下的默认命名空间(xmlns=“http://www.springframework.org/schema/beans”)下的标签,就是四个标签:< import/>、< alias/>、< bean/>、< beans/>:
    1. 我们主要学习了parseBeanDefinitionElement方法,也就是< bean/>标签的解析,将< bean />标签解析成为一个获取BeanDefinitionHolder对象,这个holder对象封装了“bean定义”——BeanDefinition、bean的名称——beanName以及bean别名数组——aliasesArray。还涉及到基于XML的beanName的生成规则、同名bean定义覆盖等知识点。
      1. parseBeanDefinitionElement方法中还调用了registerBeanDefinition方法,对解析出来的BeanDefinitionHolder进行注册,主要是注册到beanDefinitionMap、beanDefinitionNames、aliasMap这三个缓存中,这其中又涉及到别名映射的解析。
  2. parseCustomElement解析扩展标签的方法,用于解析我们引入的其他命名空间(比如http://www.springframework.org/schema/context、http://www.springframework.org/schema/aop)下的标签,比如< mvc/>、< task/>、< context/>、< aop/>……。
    1. 我们并没有介绍某个扩展标签的具体解析,因为不同的标签有不同的解析方法,下一篇文章,我们将会介绍< context:component-scan/>标签的解析源码!

相关文章:
  https://spring.io/
  Spring 5.x 学习
  Spring 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

你可能感兴趣的:(#,Spring,5.x,源码,Spring容器初始化,解析加载bean定义,bean标签解析,注册Bean定义)