Spring5.x之XmlBeanFactory源码解析-解析默认标签(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hjtlovelife/article/details/89450286

Spring5.x之XmlBeanFactory源码解析-XmlBeanFactory整体概览(一)

Spring5.x之XmlBeanFactory源码解析-解析前准备(二)

解析默认标签

        Spring中的标签包括默认标签和自定义标签两种 , 如<bean>和<aop:config>两种标签的用法以及解析方式存在着很大的不同。DefaultBeanDefinitionDocumentReader.parseBeanDefinitions方法中根据元素是否是默认标签而采取不同的解析方法,如下图:

        默认标签使用DefaultBeanDefinitionDocumentReader.parseDefaultElement方法解析,Spring默认标签有<import>、<alias>、<bean>、<beans>。
<alias>  指定类的名称 在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定。
<import>  xml文件中使用import的方式导入有模块配置文件
<bean>  创建类

<beans>  顾名思义<bean> 的集合,当然在它里面可不止<bean>。

        Spring对bean标签的解析最为复杂也最为重要,理解了bean标签的解析其它标签自然也不在话下,下面我们进入到DefaultBeanDefinitionDocumentReader.processBeanDefinition方法,如下图:


1、首先委托给BeanDefinitionDelegate.parseBeanDefinitionElement方法进行解析,返回包含我们配置文件中配置的各种属性(例如 class 、name 、id 、alias 之类的属性)的BeanDefinitionHolder实例bdHolder。
2、当返回的bdHolder不为空时若存在默认标签的子节点下再有自定义属性 , 还需要再次对自定义标签进行解析。
3、解析完成后, 需要对解析后的bdHolder进行注册,注册操作委托给BeanDefinitionReaderUtils.registerBeanDefinition 方法。
4、最后发出响应事件,通知相关监昕器,这个bean已经加载完成。

我们先进入到BeanDefinitionDelegate.parseBeanDefinitionElement方法一探究竟,如下图:

1、获取元素的id及name属性,其中name可以配置多个,;作为分隔符,如果没有配置id属性则会取name属性的第一个值作为id 
2、如果是根BeanDefinition则会检查元素id的唯一性,即指定的id是否已经被其他元素使用
3、解析其他所有属性并封装到GenericBeanDefinition实例中 
3 . 如果bean既没有指定id属性也没有指定name属性 ,那么使用BeanDefinitionReaderUtils.generateBeanName为此Bean生成beanName
4 . 将获取到的信息封装到BeanDefinitionHolder的实例中

进入到BeanDefinitionDelegate.parseBeanDefinitionElement方法查看Spring如何解析bean元素的属性

1、先解析class、parent属性,再创建用于放置bean元素属性的AbstractBeanDefinition实现类GenericBeanDefinition

2、解析默认bean元素的各种属性

3、提取description子元素

4、解析meta、lookup-method、replaced-method、constructor-arg、property、qualifier元素

BeanDefinition类图请欣赏Spring5.x之XmlBeanFactory源码解析(一)

        在Spring中有三种BeanDefinition实现:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition均继承了 AbstractBeanDefiniton ,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式 。<bean>元素标签拥有
class 、scope 、lazy-init 等配置属性, BeanDefinition 则提供了相应的beanClass 、scope  lazyInit属性, BeanD巳finition 和<bean>中的属性是一一对应 。 其中 RootBeanDefinition 是最常用的实现类,它对应一般性的<bean>元素标签,          GenericBeanDefinition 是bean配置属性定义类。在配置文件中可以定义父<bean>和子<bean>,父<bean>用 RootBeanDefinition 表示,而子<bean>用 ChildBeanDefiniton 表示,而没有父<bean>的<bean>就使用 RootBeanDefinition 表示 ,AbstractBeanDefinition 对两者共同的类信息进行抽象 。
        Spring 通过 BeanDefinition 将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些 BeanDefiniton 注册到BeanDefinitonRegistry中 。Spring容器的 BeanDefinitionRegistry就像是Spring 配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息 。

        调用BeanDefinitionReaderUtils.createBeanDefinition创建用于放置bean元素属性的GenericBeanDefinition,如果设置了class属性且classLoder也设置则会尝试加载指定的class

        创建了放置bean元素属性的GenericBeanDefinition实例后,开始进行bean元素各属性的解析,进入BeanDefinitionDelegate.parseBeanDefinitionAttributes方法查看都解析了哪些属性,如下图:

        解析bean元素中的meta子元素,并不会放在bean元素中而是通过BeanMetadataAttributeAccessor来保存提取的meta元素属性值,BeanMetadataAttribute是meta子元素在Spring容器中的内部形式。
<bean>
<meta key="special-data" value="sprecial stragey" />
</bean>


        解析bean元素中的lookup-method子元素,把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的。该方法可以用于设计一些可插拔的功能上,解除程序依赖,LookupOverride是lookup-method子子元素在Spring容器中的内部形式。
<bean class="beanClass">
    <lookup-method name="method" bean="nonSingletonBeanName"/>
</bean>

        method是beanClass中的一个方法,beanClass和method是不是抽象都无所谓,不会影响CGLIB的动态代理,根据项目实际需求去定义。nonSingletonBeanName指的是lookup-method中bean属性指向的必须是一个非单例模式的bean,当然如果不是也不会报错,只是每次得到的都是相同引用的bean,这样用lookup-method就没有意义了。

method在代码中的签名有下面的要求:
<public|protected> [abstract] <returnType> theMethodName(noArguments);

        解析bean元素中的replace-method子元素,可以在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑,ReplaceOverride是replace-method子子元素在Spring容器中的内部形式。
<bean name="replacerBean" class="MethodReplace"> </bean>
<bean name="beanName" class="beanClass">
  <replaced-method name="test" replacer="replacerBean"> </replaced-method > 
</bean>

        解析bean元素中的constructor-arg子元素,ConstructorArgumentValues.ValueHolder是constructor-arg子元素在Spring容器中的内部形式。
<bean id="provider" class="beanClass">   
    <constructor-arg index="0">   
        <value>first parameter</value>   
    </constructor-arg>   
    <constructor-arg index="1">   
        <value>second parameter</value>   
    </constructor-arg>   
</bean> 

        先获取元素上的id、type、name属性,如果指定了id属性:
1、解析constructor-arg子元素
2、用ConstructorArgumentValues.ValueHolder封装解析出来的值
3、将type、name属性值一并放在ConstructorArgumentValues.ValueHolder中,添加至BeanDefinition.getConstructorArgumentValues().addIndexedArgumentValue中
如果没有指定id属性:
1、解析constructor-arg子元素
2、用ConstructorArgumentValues.ValueHolder封装解析出来的值
3、将type、name属性值一并放在ConstructorArgumentValues.ValueHolder中,添加至BeanDefinition.getConstructorArgumentValues().addGenericArgumentValue中

下面我们看下Spring是如何解析属性值的,如下图:

1、忽略description、meta元素

2、属性元素上的ref、value属性,如果既有ref属性又有value属性或者ref、value属性有其且存在子元素,就会输出错误日志只允许包含ref属性或value属性或子元素

3、ref属性用RuntimeBeanReference表示,value属性用TypedStringValue表示

4、调用BeanDefinitionParserDelegate.parsePropertySubElement方法解析子元素,在这里实现了解析属性或constructor-arg元素的value,ref或collection子元素。

 <constructor-arg>   
    <map>   
        <entry key="someValue">   
            <value>Hello World!</value>   
        </entry>   
        <entry key="someBean">   
            <ref local="oracle"/>   
         </entry>   
    </map>   
</constructor-arg>   

       

进入BeanDefinitionParserDelegate.parsePropertySubElement方法        

        解析bean元素中的property子元素,PropertyValue是property子元素在Spring容器中的内部形式。

        解析bean元素中的qualifier子元素,我们接触更多的是注解形式,在使用Spring 自动注入时,Spring 容器中匹配的候选Bean必须有且仅有一个,AutowireCandidateQualifier是qualifier子元素在Spring容器中的内部形式。

<bean id="beanId" class="beanClass">
    <qualifier type="qualifierBeanClass" value="beanName"></qualifier>
</bean>

        至此bean元素解析完成,所有的属性以及子元素全部封装到GenericBeanDefinition,XML中所有的配置都可以在 GenericBeanDefinition的实例类中都能找到对应的配置 。

猜你喜欢

转载自blog.csdn.net/hjtlovelife/article/details/89450286