Spring源码深度解析(郝佳)-学习-字符串值解析-property解析

我们来分析一下User对象的解析


<bean id="user" class="com.spring_101_200.test_111_120.test_112_env.User">
    <property name="username" value="${username}" />
    <property name="age" value="${age}" />
</bean>

我们继续默认标签的解析

/**
 * 使用Spring的Bean规则从文档的根元素开始Bean定义的文档对象解析
 */ 
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	// Bean 的定义的文档对象使用了Spring默认的命名空间, 自定义的命名空间的还是自定义的命名空间,
	// 并与Spring中固定的命名空间 "http://www.springframework.org/schema/beans" ,进行比对,
	// 如果一致是默认,否则就认为是自定义的 
	if (delegate.isDefaultNamespace(root)) {
		// 获取Bean定义的文档对象根元素的所有子节点 
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				// 获取文档节点的XML元素的节点 
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					// 开始默认标签解析 , 如<bean id="test" class="test.TestBean"> 
					parseDefaultElement(ele, delegate);
				}
				else {
					// 如果没有使用Spring默认的xml命名空间,则使用用户自定义的解析规则解析元素的节点,开始自定义标签两种格式的区分
					// Spring 拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,则使用 parseDefaultElement方法
					// 进行元素解析,否则使用 parseCustomElment 元素进行解析
					// 如:<ltx:annotation-driven/>
					//     <context:property-placeholde /> 
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		// 文档对象根节点没有使用Spring默认的命名空间
		// 使用自定义的解析规则解析文档的根节点 
		delegate.parseCustomElement(root);
	}
}

DefaultBeanDefinitionDocumentReader.java

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		doRegisterBeanDefinitions(ele);
	}
}

DefaultBeanDefinitionDocumentReader.java

/**
 * 1.首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的bdHolder
 *   ,经过这个方法后bdHolder实例己经包含我们配置文件中配置的各个属性了,例如class,name,id,alias之类的属性
 * 2.当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义的属性进行标签解析
 * 3.解析完成后,需要对解析后的bdHolder进行注册,更新,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法
 * 4.最后发出响应事件,通知相关的监听器,这个bean己经加载好了
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义封装类
	// 对文档中的元素解析由BeanDefinitionParserDelegate实现
	// BeanDefinitionHolder bdHolder = deletate.parseBeanDefinitionElement(element)
	// BeanDefinitionHolder是对
	if (bdHolder != null) {
		/***
		 * 如果需要的话就对 beanDefinition 进行装饰,那么这句代码的作用就是什么功能呢?
		 * 这句代码的使用场景如下:
		 * <bean id="test" class="test.MyClass">
		 *     <mybean:user username="aaaa"/>
		 * </bean>
		 * 当 Spring 中的 bean 使用了默认的标签配置,但是其中的子元素却使用了自定义的配置,这句代码就起作用了,可能会有人会疑问,
		 * 之前讲过,对 bean 的解析分成两种类型,一种是默认的类型解析,另一种是自定义类型解析,这不正是自定义类型解析吗?为什么会在
		 * 默认的类型解析中单独的添加一个方法的处理呢,确实,这个问题很让人迷惑,但是,不知道聪明的读者有没有发现,这个自定义类型并不是
		 * 以 Bean 的形式出现的呢?我们之前讲过两种类型的不同处理只是针对 bean 的,这里我们看到,这个自定义类型其实是属性,好了,我们
		 * 我们继续分析这个代码
		 */
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// 向Spring Ioc容器注册解析得到的bean定义,这是Bean定义向Ioc容器注册的入口
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// 通过 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 来完成此工作,这里实现
		// 只为扩展,当程序开发人员需要对注册的BeanDefinition事件进行监听时可以通过注册监听器方式将处理的逻辑写入监听器中,目前在Spring
		// 并没有对此事做任何逻辑处理
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

BeanDefinitionParserDelegate.java

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
	return parseBeanDefinitionElement(ele, null);
}

BeanDefinitionParserDelegate.java

/**
 * 解析Bean配置信息中的元素,这个方法中主要处理的元素的id,name,别名属性
 */ 
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    // 获取元素中的id属性值 
    String id = ele.getAttribute(ID_ATTRIBUTE);
    // 获取元素的name属性值 
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    // 获取bean元素的alias属性值  
    List aliases = new ArrayList();
    // 将元素的中的所有alias属性值放入到别名中 
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
    String beanName = id;
    // 如果元素中没有配置id属性,将别名中的第一个值赋值给beanName 
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        beanName = aliases.remove(0);
        if (logger.isDebugEnabled()) {
            logger.debug("No XML 'id' specified - using '" + beanName +
                    "' as bean name and " + aliases + " as aliases");
        }
    }
    // 检查元素所配置的id或者name唯一性 
    if (containingBean == null) {
        //检查元素的所配置的id,name或者别名是否重复 
        checkNameUniqueness(beanName, aliases, ele);
    }
    // 详细对元素中的配置的bean定义进行解析 
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        if (!StringUtils.hasText(beanName)) {
            try {
                if (containingBean != null) {
                   // <bean>元素,则解析的Bean生成一个唯一的beanName并注册 
                    beanName = BeanDefinitionReaderUtils.generateBeanName(
                            beanDefinition, this.readerContext.getRegistry(), true);
                } else {
                    // 如果元素没有配置id,别名或者name,且包含子元素
                    // 元素,则将解析的Bean生成一个唯一的BeanName并注册 
                    beanName = this.readerContext.generateBeanName(beanDefinition);

                    String beanClassName = beanDefinition.getBeanClassName();
                    // 为解析的Bean使用别名注册时,为了向后兼容
                    // Spring 1.2 /2.0 给别名添加后缀 
                    if (beanClassName != null &&
                            beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                            !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                        aliases.add(beanClassName);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Neither XML 'id' nor 'name' specified - " +
                            "using generated bean name [" + beanName + "]");
                }
            } catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
            }
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    // 当解析出错时,返回null 
    return null;
}

BeanDefinitionParserDelegate.java

/**
 * 详细对元素中配置的Bean定义的其他的属性进行解析
 * 由于上面的方法已经对Bean的id,name和别名属性进行了处理
 * 该方法主要是处理除了这三个以外的其他的属性
 * 【注意】在解析元素的过程中没有创建和实例化对象,只是创建了Bean元素的定义类BeanDefinition,将元素中的信息
 * 设置到了BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象
 * 在对一些配置(如meta,qualifier等)的解析,我们在Spring中使用得不多,在使用Spring元素时,配置最多的就是子元素
 */
public AbstractBeanDefinition parseBeanDefinitionElement(
        Element ele, String beanName, BeanDefinition containingBean) {
    // 记录解析元素
    this.parseState.push(new BeanEntry(beanName));
    // 这里只读取元素中配置的class名字,然后载入BeanDefinition中
    // 只是记录配置的class名字,不做实例化,对象的实例化在依赖注入的时候完成
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }

    try {
        String parent = null;
        // 如果元素中配置了parent属性,则获取 parent属性的值
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }
        // 根据元素配置的class名称和parent属性值创建BeanDefinition
        // 为载入的Bean定义信息做准备
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        // 对当前的元素中配置的一些属性进行解析和设置,如果配置了单态(singleton)属性等
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 为<bean>元素解析的Bean设备描述信息
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        // 为<bean>元素的meta(元信息进行解析)
        parseMetaElements(ele, bd);
        // 为<bean>元素的lookup-Method属性进行解析
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 为<bean>元素的replaced-Method属性进行解析
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        //解析<bean>元素构造方法设置
        parseConstructorArgElements(ele, bd);
        //解析<bean>元素的<property>设置
        parsePropertyElements(ele, bd);
        // 解析<bean>元素的qualifier属性
        parseQualifierElements(ele, bd);
        // 为当前的解析了Bean设置所需要的资源和依赖对象
        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));
        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;
}
## BeanDefinitionParserDelegate.java

/**
 * Parse property sub-elements of the given bean element.
 * 解析元素中的元素
 * 7.解析子元素property
 * parsePropertyElement函数完成了对property属性的提取,property使用方式如下
 * <bean id="test" class="test.TestClass">
 *      <property name="testStr" value="aaa">
 * </bean>
 * 或者
 * <bean id="a">
 *      <property name="p">
 *             <list>
 *                 <value> aaa</value>
 *                 <value> bb </value>
 *             </list>
 *      </property>
 * </bean>
 *
 */
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
    // 获取元素中的所有的子元素
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 如果子元素中元素的子元素,则调用解析子元素的方法解析
        if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
            parsePropertyElement((Element) node, bd);
        }
    }
}

BeanDefinitionParserDelegate.java

/**
 * 可以看到上面的函数注入方式不同的是将返回值使用PropertyValue进行封装,并记录在BeanDefinition中的propertyValue属性中
 */
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    // 获取子元素中的name值
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
    }
    this.parseState.push(new PropertyEntry(propertyName));
    try {
        // 如果一个Bean中已经存在同名的元素存在,则不进行解析,直接返回
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        //解析获取的元素的值
        Object val = parsePropertyValue(ele, bd, propertyName);
        //根据元素的名字和值创建实例
        PropertyValue pv = new PropertyValue(propertyName, val);
        //解析元素中的meta属性
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);
    } finally {
        this.parseState.pop();
    }
}

BeanDefinitionParserDelegate.java

/**
*
* 从代码上来看,对函数的属性元素的解析,经历了以下的几个过程
* 1.略过description或者meta
* 2.提取constructor-arg上的ref和value属性,以便于根据规则验证正确性,其规则为在constructor-arg 上不存在以下的情况
* 同时既有ref又有value属性
* 存在ref属性或者value属性且又有子元素
* 3.ref属性的处理,使用RunTimeBeanReference封装对应的ref名称,如:
* <constructor-arg ref="a">
*
* </constructor-arg>
* 4.value属性的处理,使用TypeStringValue封装,如:
* <constructor-arg value="a">
* 5.子元素的处理
* <constructor-arg >
*      <map>
*          <entry key="key" value="value">
*      </map>
* </constructor-arg>
* 而对于子元素的处理,例如,这里反映到的在构造函数中嵌入了子元素map是怎样实现的呢?parsePropertySubElement中对实现了对各种子元素的处理
 */
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
   String elementName = (propertyName != null) ?
           " element for property '" + propertyName + "'" :
           " element";
   // 获取中的所有的子元素,只能是ref,value,list,etc中的一种类型
   NodeList nl = ele.getChildNodes();
   Element subElement = null;
   for (int i = 0; i < nl.getLength(); i++) {
       Node node = nl.item(i);
       // 子元素是description和meta属性 不做处理
       if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
               !nodeNameEquals(node, META_ELEMENT)) {
           if (subElement != null) {
               error(elementName + " must not contain more than one sub-element", ele);
           } else {
               // 当property元素包含子元素
               subElement = (Element) node;
           }
       }
   }
   // 解析constructor-arg 的ref 属性
   boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
   // 解析constructor-arg 上的value属性
   boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
   // 判断属性值是ref还是value,不允许既是ref 又是value
   if ((hasRefAttribute && hasValueAttribute) ||
           ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
       /**
        * 在constructor-arg上不存在:
        * 1.同时既有ref属性又有value属性
        * 2.存在ref属性或者value属性且又有子元素
        */
       error(elementName +
               " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
   }
   // 如果属性值是ref ,创建一个ref 的数据对象,RuntimeBeanReference,这个对象封装了ref
   if (hasRefAttribute) {
       String refName = ele.getAttribute(REF_ATTRIBUTE);
        if (!StringUtils.hasText(refName)) {
           error(elementName + " contains empty 'ref' attribute", ele);
       }
       //一个指向运行是所依赖对象的引用 ,ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
       RuntimeBeanReference ref = new RuntimeBeanReference(refName);
       ref.setSource(extractSource(ele));
       return ref;
       // 如果属性值是value,创建一个value数据对象,typedStringValue,这个对象封装了value
   } else if (hasValueAttribute) {
       // 一个持有String类型的对象
       TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
       // 设置这个value的数据对象被当前对象所引用
       valueHolder.setSource(extractSource(ele));
       return valueHolder;
   } else if (subElement != null) {
       // 解析子元素
       return parsePropertySubElement(subElement, bd);
   } else {
       // 属性值既不是ref也不是value,解析出错,返回null
       error(elementName + " must specify a ref or value", ele);
       return null;
   }
}

最终将属性添加到MutablePropertyValues 的 propertyValueList中
Spring源码深度解析(郝佳)-学习-字符串值解析-property解析

在这里,我们得到了User对象的BeanDefinition对象,己经完成了对property属性的解析

本文github地址是https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_101_200/test_111_120/test_112_env/