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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料
  • 前言

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

    • BeanDefinition的载入和解析

    • 及其子节点的解析

    前文讲了,这里写了所有Spring可以配置的属性、节点等等。比如我们常见的:等,也包括我们很少用的元信息(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; 
    
               } 
    

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

    • 元素的解析

    我们重点看parsePropertyElements方法。

    代码1.3:BeanDefinitionParserDelegate类的parsePropertyElements方法

    
              //该方法--调用入口 
    
              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); 
    
               } 
    
               } 
    
               } 
    
               //该方法--解析元素 
    
               public void parsePropertyElement(Element ele, BeanDefinition bd) { 
    
               //获取元素的名字 
    
               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); 
    
               //解析元素中的属性 
    
               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) ? 
    
               " element for property '" + propertyName + "'" : 
    
               " element"; 
    
               //获取的所有子元素,只能是其中一种类型: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 {//当前元素包含有子元素 
    
               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) {//如果当前元素还有子元素 
    
               return parsePropertySubElement(subElement, bd); 
    
               } 
    
               else { 
    
               // 既不是"ref"又不是"value",报错 
    
               error(elementName + " must specify a ref or value", ele); 
    
               return null; 
    
               } 
    
               } 
    

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

    • 子元素的解析

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

    
              //该方法--调用入口 
    
              public Object parsePropertySubElement(Element ele, BeanDefinition bd) { 
    
               return parsePropertySubElement(ele, bd, null); 
    
               } 
    
              //该方法--解析元素中ref,value或者集合等子元素 
    
              public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { 
    
               //如果没有使用Spring默认的命名空间,则使用用户自定义的规则解析内嵌元素 
    
               if (!isDefaultNamespace(ele)) { 
    
               return parseNestedCustomElement(ele, bd); 
    
               } 
    
               //如果子元素是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)) { 
    
               //获取元素中的bean属性值,引用其他解析的Bean的名称 
    
               //【可以不再同一个Spring配置文件中】 
    
               String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); 
    
               boolean toParent = false; 
    
               if (!StringUtils.hasLength(refName)) { 
    
               //如果没有bean,获取元素中的local属性值,引用同一个Xml文件中配置的Bean的id, 
    
               //【local和ref不同,local只能引用同一个配置文件中的Bean】 
    
               refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); 
    
               if (!StringUtils.hasLength(refName)) { 
    
               //如果也没有local,获取元素中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  element", ele); 
    
               return null; 
    
               } 
    
               } 
    
               } 
    
               if (!StringUtils.hasText(refName)) { 
    
               error(" element contains empty target attribute", ele); 
    
               return null; 
    
               } 
    
               /创建ref类型数据,指向被引用的对象 
    
               RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); 
    
               ref.setSource(extractSource(ele)); 
    
               return ref; 
    
               } 
    
               //如果子元素是,使用解析ref元素的方法解析 
    
               else if (nodeNameEquals(ele, IDREF_ELEMENT)) { 
    
               return parseIdRefElement(ele); 
    
               } 
    
               //如果子元素是,使用解析value元素的方法解析 
    
               else if (nodeNameEquals(ele, VALUE_ELEMENT)) { 
    
               return parseValueElement(ele, defaultValueType); 
    
               } 
    
               //如果子元素是null,为设置一个封装null值的字符串数据 
    
               else if (nodeNameEquals(ele, NULL_ELEMENT)) { 
    
               TypedStringValue nullHolder = new TypedStringValue(null); 
    
               nullHolder.setSource(extractSource(ele)); 
    
               return nullHolder; 
    
               } 
    
               //如果子元素是,使用解析array集合子元素的方法解析 
    
               else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { 
    
               return parseArrayElement(ele, bd); 
    
               } 
    
               //如果子元素是,使用解析list集合子元素的方法解析 
    
               else if (nodeNameEquals(ele, LIST_ELEMENT)) { 
    
               return parseListElement(ele, bd); 
    
               } 
    
               //如果子元素是,使用解析set集合子元素的方法解析 
    
               else if (nodeNameEquals(ele, SET_ELEMENT)) { 
    
               return parseSetElement(ele, bd); 
    
               } 
    
               //如果子元素是,使用解析map集合子元素的方法解析 
    
               else if (nodeNameEquals(ele, MAP_ELEMENT)) { 
    
               return parseMapElement(ele, bd); 
    
               } 
    
               //如果子元素是,使用解析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对象。

    • 子元素解析

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

    
              public List parseListElement(Element collectionEle, BeanDefinition bd) { 
    
               //获取元素中的value-type属性,即获取集合元素的数据类型 
    
               String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 
    
               //获取集合元素中的所有子节点 
    
               NodeList nl = collectionEle.getChildNodes(); 
    
               //Spring中将List封装为ManagedList 
    
               ManagedList target = new ManagedList(nl.getLength()); 
    
               target.setSource(extractSource(collectionEle)); 
    
               target.setElementTypeName(defaultElementType); 
    
               target.setMergeEnabled(parseMergeAttribute(collectionEle)); 
    
               //具体的元素解析 
    
               parseCollectionElements(nl, target, bd, defaultElementType); 
    
               return target; 
    
               } 
    
               //具体解析集合元素,都使用该方法解析 
    
              protected void parseCollectionElements( 
    
               NodeList elementNodes, Collection 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的配置,大家结合着来看,可能会稍微好消化一点点。20191017100230\_1.png 来看一个sessionFactory的配置,找了半天觉得这个很典型,基本上Spring源码里面准备解析的都有了。来看下代码

        
                
                
                
                             
                     
                    org.hibernate.dialect.PostgreSQLDialect
                        10
                        true
                        false
                
            
                
                
                        
                            com.xxx.xxx
                        com.xx.xxxx
                    
                
            

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


    来源:[]()

    赞(0) 打赏
    版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » 【Spring源码–IOC容器的实现】(三)BeanDefinition的载入和解析【II】

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址

    © 2014 - 2020 Java 技术驿站   网站地图  | 

    icp 湘ICP备14000180

    >>> 网站已平稳运行:

    觉得文章有用就打赏一下文章作者

    支付宝扫一扫打赏

    微信扫一扫打赏