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

    xiaoxiao2026-09-27  10

    前言

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

    BeanDefinition的载入和解析

    <bean>及其子节点的解析

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

    代码1.1:BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法

    public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { ... String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); //这里面设置scope、abstract、lazy-init等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); //设置property属性 parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); ... }

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

    代码1.2:BeanDefinitionParserDelegate类的parseBeanDefinitionAttributes方法

    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { //设置scope属性 if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Specify either 'scope' or 'singleton', not both", ele); } } else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (containingBean != null) { //设置默认,也就是空 bd.setScope(containingBean.getScope()); } //设置abstract if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } //设置lazy-init属性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); //设置自动装配属性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); //设置dependency-check String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); //设置depends-on if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS)); } //设置autowire-candidate String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } //设置primary if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //init-method if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); if (!"".equals(initMethodName)) { bd.setInitMethodName(initMethodName); } } else { if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } } //设置destroy-method if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); if (!"".equals(destroyMethodName)) { bd.setDestroyMethodName(destroyMethodName); } } else { if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } } //设置工厂方法factory-method if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //设置工厂类factory-bean if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }

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

    <property>元素的解析

    我们重点看parsePropertyElements方法。

    代码1.3:BeanDefinitionParserDelegate类的parsePropertyElements方法

    //该方法--调用入口public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //获取<Bean>元素中所有的子元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果子元素是<property>子元素,则调用解析<property>子元素方法解析 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } } //该方法--解析<property>元素 public void parsePropertyElement(Element ele, BeanDefinition bd) { //获取<property>元素的名字 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中已经有同名的property存在,则不进行解析,直接返回。 //即如果在同一个Bean中配置同名的property,则只有第一个起作用 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //解析获取property的值 Object val = parsePropertyValue(ele, bd, propertyName); //根据property的名字和值创建property实例 PropertyValue pv = new PropertyValue(propertyName, val); //解析<property>元素中的属性 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }

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

    代码1.4:BeanDefinitionParserDelegate类的parsePropertyValue方法

    public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; //获取<property>的所有子元素,只能是其中一种类型:ref,value,list等 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; } } } //判断property的属性值是ref还是value,不允许既是ref又是value boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { 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); } //指向运行时所依赖对象 的引用 RuntimeBeanReference ref = new RuntimeBeanReference(refName); //设置这个ref的数据对象是被当前的property对象所引用 ref.setSource(extractSource(ele)); return ref; } //如果属性是value,创建一个value的数据对象TypedStringValue,这个对象封装了value信息 else if (hasValueAttribute) { //持有String类型值的对象 [这里一般我们都配class的路径,因为不实例化,所以这样处理] TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null) {//如果当前<property>元素还有子元素 return parsePropertySubElement(subElement, bd); } else { // 既不是"ref"又不是"value",报错 error(elementName + " must specify a ref or value", ele); return null; } }

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

    <property>子元素的解析
    在上面方法的最后对于<property>元素的子元素通过parsePropertySubElement方法解析,我们继续分析该方法的源码,了解其解析过程. 代码1.5:BeanDefinitionParserDelegate类的parsePropertySubElement方法 //该方法--调用入口public Object parsePropertySubElement(Element ele, BeanDefinition bd) { return parsePropertySubElement(ele, bd, null); }//该方法--解析<property>元素中ref,value或者集合等子元素 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { //如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析内嵌元素 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } //如果子元素是bean,则使用解析<Bean>元素的方法解析 【回到最初的那个bean解析方法】 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } //如果子元素是ref,ref中只能有以下3个属性:bean、local、parent else if (nodeNameEquals(ele, REF_ELEMENT)) { //获取<property>元素中的bean属性值,引用其他解析的Bean的名称 //【可以不再同一个Spring配置文件中】 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { //如果没有bean,获取<property>元素中的local属性值,引用同一个Xml文件中配置的Bean的id, //【local和ref不同,local只能引用同一个配置文件中的Bean】 refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { //如果也没有local,获取<property>元素中parent属性值,引用父级容器中的Bean的id // 【parent只能引用同一个父级Context中的bean】. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean', 'local' or 'parent' is required for <ref> element", ele); return null; } } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } /创建ref类型数据,指向被引用的对象 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } //如果子元素是<idref>,使用解析ref元素的方法解析 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //如果子元素是<value>,使用解析value元素的方法解析 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //如果子元素是null,为<property>设置一个封装null值的字符串数据 else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //如果子元素是<array>,使用解析array集合子元素的方法解析 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //如果子元素是<list>,使用解析list集合子元素的方法解析 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //如果子元素是<set>,使用解析set集合子元素的方法解析 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //如果子元素是<map>,使用解析map集合子元素的方法解析 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //如果子元素是<props>,使用解析props集合子元素的方法解析 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } } 上面的代码我们可以学到哪些东西呢?Spring对上面这些子元素的解析,也就是我们可以给property配置的全部类型。如:bean,ref,idref,value,null,以及各种集合类。对于bean和ref在上面的方法中就有,直接创建一个引用的bean对象。
    <list>子元素解析
    那么对于集合类型的,我们再看一个例子。 代码1.6:BeanDefinitionParserDelegate类的parsePropertySubElement方法 public List parseListElement(Element collectionEle, BeanDefinition bd) { //获取<list>元素中的value-type属性,即获取集合元素的数据类型 String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); //获取<list>集合元素中的所有子节点 NodeList nl = collectionEle.getChildNodes(); //Spring中将List封装为ManagedList ManagedList<Object> target = new ManagedList<Object>(nl.getLength()); target.setSource(extractSource(collectionEle)); target.setElementTypeName(defaultElementType); target.setMergeEnabled(parseMergeAttribute(collectionEle)); //具体的<list>元素解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } //具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析 protected void parseCollectionElements( NodeList elementNodes, Collection<Object> target, 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)) { //将解析的元素加入集合中,递归调用下一个子元素 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); } } } 经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition【AbstractBeanDefinition】。那么还有其他属性的解析,大家可以自己看看。一定要多看看对于元素的解析,不然依赖注入的时候你整不明白这些属性从哪里来的。

    BeanDefinition载入总结

    来一个代码的流程图,结合上一篇文章,顺便简单串一下主要方法。还有一个问题大家注意下,我们的代码一步步走下去是一点点细化的过程,从web.xml读取的可能是多个路径,一个路径下面可能有多个文件(Resource),一个文件里面有多个Bean,一个Bean里面有多个Property,一个Property里面只能有一个Ref,Value,List。这个过程大家要注意思考。我这里先放一张代码的流程图,再给大家举个比较复杂点的Bean的配置,大家结合着来看,可能会稍微好消化一点点。     来看一个sessionFactory的配置,找了半天觉得这个很典型,基本上Spring源码里面准备解析的都有了。来看下代码<bean id="sessionFactory" class="com.xxx"> <!--dataSource属性指定要用到的数据源,因此在Hibernate的核心配置文件中就无需再配置与数据库连接相关的属性--> <property name="dataSource" ref="xxxx" /> <property name="hibernateProperties"> <props> <!-- Hibernate基本配置 --> <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> <prop key="hibernate.connection.pool_size">10</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> </props> </property> <!-- 加入使用注解的实体类 --> <property name="annotatedClasses"> <list> <value>com.xxx.xxx</value> <value>com.xx.xxxx</value> </list> </property> </bean>我删了很多的配置,为了项目信息保密嘛,不过这个已经很有代表性了。我们可以看到Bean的基本配置,Property三种配置都有了,我们再结合上面对bean的解析、Property的解析、list的解析。  BeanDefinition的载入内容就先这么多,以后再有想法再补充吧!后面的注册就显得相对简单了,大家如果看到这里,注册基本不是问题。
    转载请注明原文地址: https://ju.6miu.com/read-1312381.html
    最新回复(0)