Spring源码解析之一---将Dom实例解析成BeanDefinition对象

XmlBeanFactory的uml图

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第1张图片首先了解下几个类的职能
BeanDefinitionRegistry: 定义Bean的常规操作,来注册BeanDefinition, 内部就是用一个 Map 实现.
SimpleBeanDefinitionRegistry 是 BeanDefinitionRegistry 一个简单的实现。只提供注册表的功能,不提供工厂其他功能。
DefaultListableBeanFactory:ConfigurableListableBeanFactory(其实就是 BeanFactory ) 和 BeanDefinitionRegistry 接口的默认实现:一个基于 BeanDefinition 元数据的完整 bean 工厂。

XmlBeanDefinitionReader:相当于是个统筹规划者

将Xml读取成Dom实例将Dom实例注册到BeanDefinition都使用其他类
Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第2张图片

DefaultDocumentLoader:用于将Xml配置文件解析成Domcument实例

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第3张图片

BeanDefinitionDocumentReader:用于将Document实例注册到BeanDefinitionRegistry

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第4张图片该类中有一个委托属性,即Dom实例中每个标签的解析委托给了delegate属性。

BeanDefinitionParserDelegate:用于解析 XML bean 定义的有状态委托类

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第5张图片

总体需求:将Domcument实例解析成BeanDefinition对象并注册

1、需求划分及Spring的解决方案

1、解析根标签的默认属性

将beans标签的默认属性的解析委托给delegate属性;解析的默认属性存储在BeanDefinitionParserDelegate类的beanDefinitionDefaults属性中。

首先回顾下beans标签中可填的属性有哪些

XSD风格的Xml文件以beans标签开头,其属性可以设置一些默认行为,例如

  1. default-lazy-init:
可选值 功能说明
false(default) spring在启动过程导致在启动时候,会默认加载整个对象实例图,比较耗时,故Spring项目启动都需要较长的时间
true 不会加载整个对象实例图,启动速度较快,但生产环境一般设置为false
  1. default-merge:较为少用,不作分析
  2. default-autowire:指定bean标签默认使用哪种自动注入
可选值 功能说明
no 默认不使用autowiring。 必须显示的使用”“标签明确地指定bean。
byName 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。
byType 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果你不希望这样,那么可以通过设置 dependency-check=”objects”让Spring抛出异常。
constructor 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
autodetect 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
  1. default-autowire-candidates:告诉bean的容器,哪些可以做自动注入,哪些不可以,它支持按照bean的名称模糊匹配
  2. default-init-method: 指定所有bean的默认初始化方法
  3. default-destroy-method:指定所有bean的默认销毁方法

属性是如何填充的?

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第6张图片

  • lazyInit,merge,autowire若未设置属性或者属性值为default(又或者属性值长度为0),则前2者默认false,autowire默认为no
  • default-autowire-candidates,default-init-method,default-destroy-method若设置了,则为设置的值,否则为空
  • source属性(该属性可以忽略,没什么用处)最为特别,其由readerContext对象的sourceExtractor属性,通过Resource对象和Element对象(即root,也就是beans标签对应的Element对象)提取源,将提取的源赋值给source。然而提取的源为null,所以可以暂时忽略该属性,因为后续整个过程没有使用到。

2、预留解析前后的操作,供用户自己制定

模板设计模式,在BeanDefinitionDocumentReader接口中预留了2个方法,在Dom解析成BeanDefinition的前后添加操作处理

3、解析根级别标签

3.1、解析自定义标签(少用,本文不分析)

3.2、解析默认名称空间的标签

1、import(本文不分析)
2、alias标签解析
3、bean标签解析

首先看一个bean的配置例子,该例子尽量覆盖所有属性和子标签

    <bean id="id1" name="beanName1;beanName2;beanName3" class="class1" parent="parent1" abstract="true/false"
          lazy-init="true/false/default" scope="singleton/prototype"
          autowire="default/byName/byType/constructor/autodetect/no" depends-on="dependentBeanName"
          autowire-candidate="default/true/false" primary="true/false"
          init-method="init" destroy-method="destroy">

        <meta key="metaKey1" value="metaValue1"/>
        <meta key="metaKey2" value="metaValue2"/>
        
<!--     1、使用constructor-arg属性参数注入
        	 constructor-arg标签属性
        	 index: 参数位置索引(用于指定对应的形参)
        	 name: 参数形参名(用于指定对应的形参)
        	 type: 限定类型
        	 ref: 如果传入引用类型 -->
<!--   	 index属性,普通类型值-->
        <constructor-arg index="0" value="value1">
            <description></description>
        </constructor-arg>
<!--    	index属性,引用类型值-->
        <constructor-arg index="1" ref="ref1"/>
<!--        name,type,普通类型值-->
        <constructor-arg name="argName1" type="int" value="1"/>
<!--        name,type,引用类型值-->
        <constructor-arg name="argName2" type="User" ref="user1"/>
        
<!--    2、注入到集合
       	 注入到list或set(简单类型+`String`)-->
        <constructor-arg name="...">
            <list>
                <value>c</value> <value>f</value> <value>r</value>
            </list>
        </constructor-arg>
<!--    	注入到list或set(对象类型)-->
        <constructor-arg name="...">
            <list>
                <ref bean="person1"/> <ref bean="person0"/> <ref bean="person1"/>
            </list>
        </constructor-arg>

<!--    3、注入到map;
        	如果要注入的元素是对象,则使用属性:`key-ref="..." value-ref="..." value-type="..."`来定义`<entry>`-->
        <constructor-arg name="...">
            <map>
                <entry key="1" value="value of 1"/> <entry key="2" value="value of 2"/> <entry key="3" value="value of 2"/>
            </map>
        </constructor-arg>
<!--    4、注入到数组(Array)
       	 同样地,复杂对象使用`<ref bean=""/>`-->
        <constructor-arg name="">
            <array>
                <value>1</value> <value>2</value> <value>3</value>
            </array>
        </constructor-arg>
        
        <property name="name1" value="value2"/>
        <property name="name2" ref="ref2" />
    </bean>
  1. id和name属性
  • id:id的属性值作为该bean的beanName
  • name:name属性的值允许配置多个,用,或者;分割;若id为空,则name值中第一个作为beanName,其余的作为该bean的别名

还需要检测该bean的beanName别名在该容器中是否唯一

  1. 解析其他属性
  • className:class属性值
  • parent:parent属性值
  • scope:scope属性值,若目前解析的bean是内部bean,并且scope属性不存在则继承外部bean的scope
  • abstract:默认false,否则为设置的值
  • lazy-init:若设置为default或者没有设置该属性,则继承beans默认配置;
  • autowire:该值是int类型,故需要进行转换
  • depends-on:将传入的值按指定分隔符分割为String数组进行存储;
  • autowire-candidate:若未设置,则获取beans的default-autowire-candidate,若当前beanName在其中,则为true,否则false;
  • parimary:默认false,否则为设置的值
  • init-method:设置了则为设置的值,否则为null;
  • destory-method:同上
  • factory-method:同上
  • factory-bean:同上
  1. meta子标签
    遍历bean标签的所有子标签,对meta则进行处理;

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第7张图片
BeanMetadataAttribute

  • name:meta标签的key属性值
  • value:meta标签的value属性值
  • source:提取源,也就是标志该attributes属性从哪个Element实例提取的,实际为null

AttributeAccessorSupport

  • attributes(Map ):其Map中一个Entry对应一个meta标签,Entry的key为name,value为BeanMetadataAttribute实例
  1. description
    description1标签只能存在一个;其值(该例子中为description1)存储在description属性

  2. 方法重写—LookupOverride
    Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第8张图片

MethodOverride:

  • methodName:lookup-override标签的name属性值
  • source:提取来源标签

LookupOverride

  • beanName:方法返回的bean名称(该bean在容器中)

MethodOverrides:

  • overrides:该属性位于AbstractBeanDefinition类中,用于存储MethodOverride集合。
  1. 方法重写—ReplaceOverride

用法介绍

//类
public class TestChangeMethod {
   
		public void changeMe(){
   
                     System.out.println("changeMe");
             }
}

public class TestMethodReplacer implements MethodReplacer{
   
        @Override
       public Object reimplement(Object obj, Method method, Object[] args)throws Throwable {
   
               System.out.println("我替换了原有的方法");
               return null;
       }
}

//xml配置
	<bean id="testChangeMethod" class="test.replacemethod.TestChangeMethod">
		<replaced-method name="changeMe" replacer="replacer"/>
	</bean>
	<bean id="replacer" class="test.replacemethod.TestMethodReplacer"/>


// 最终使用容器获取该类调用changeMe方法的输出结果是:
// 我替换了原有的方法

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第9张图片
MethodOverride:

  • methodName:replaced-method标签的name属性值
  • source:提取来源标签

ReplaceOverride

  • methodReplacerBeanName:存储replaced-method标签中replacer的属性值
  1. constructor-arg

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第10张图片对于构造函数的存储;分为2种类型:1.不带index属性的标签;2.带index属性的标签
ConstructorArgumentValues

  • indexedArgumentValues:存储带index属性的constructor-arg标签解析出来的值;其Map类型中Entry的key为index,value为ValueHolder实例。
  • genericArgumentValues:存储不带带index属性的constructor-arg标签解析出来的值;

因为值的类型多种多样,故Spring设置了ValueHolder用于存储解析出来的值。
ConstructorArgumentValues.ValueHolder

  • value:解析出来的值(从constructor-arg子标签、value属性、ref属性解析出来的值)
  • type:constructor-arg标签的type属性值
  • name:constructor-arg标签的name属性值

下面重点了解下constructor-arg标签的值被解析后是如何存放的

首先先总览一下解析值所涉及的类

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第11张图片
接下来逐一详细分析,若无法理解可先跳过,先查看后面的方法解析

  • ref属性
    Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第12张图片
    RuntimeBeanReference

    • toParent:默认为false,只有ref标签才会动用该属性,ref属性默认为false
    • beanName:ref属性值
  • value属性:
    Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第13张图片
    TypedStringValue

    • value:value属性值
  • bean子标签(这三个属性就不讲解了,就是bean标签的存储形式)
    BeanDefinitionHolder

    • beanDefinition:
    • aliases:
    • beanName:
  • ref子标签(该标签parent属性和bean属性二者必须有一个)
    RuntimeBeanReference

    • toParent:存在parent属性则为true
    • beanName:ref属性值
  • idref子标签:从来没见过,也少用,不分析

  • value子标签
    TypedStringValue

    • value:value标签的文本值
    • targetType:Class实例(ReadContext实例中beanClassLoader不为空)或者类型名称,此处若为空会使用方法参数的defaultTypeName
    • specifiedTypeName:value标签type属性值(没有任何判断,直接赋值)
  • null子标签
    TypedStringValue

    • value:null值

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第14张图片

  • array子标签
    ManagedList
    • elementTypeName:array标签的value-type属性值
    • mergeEnabled:array标签的merge属性值
      ArrayList
    • elementData:array标签子标签的值集合(该集合的类型和构造函数的值类型一样,使用的是重载函数,不同的是此处的defaultTypeName不为空,为elementTypeName)
    • size:子标签的数量
  • list子标签:和上述几乎相同,不再分析
  • set子标签:和上述几乎相同,不再分析
  • map子标签:少用,不影响后续理解,不分析
  • pros子标签:少用,不影响后续理解,不分析

注意:

  • 若设置了index属性,则子标签中只能有一个赋值标签(如ref, value, list等);
  • 主要是为了处理属性ref,value以及赋值标签之间的关系,三者只能存一,因为都是给构造器某个参数注入值,只需要一个值就可。

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第15张图片

  1. property
    MutablePropertyValues
  • propertyValueList:List集合
    PropertyValue
  • name:property标签的name属性值
  • value:property的值

该解析过程和constructor-arg类似,不再赘述

4、注册bean

2、重要的类解析

我们最终解析的结果都存放在该对象中,在此先对该对象做分析

1、解析的BeanDefinition所涉及的类

GenericBeanDefinition UML

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第16张图片GenericBeanDefinition用于承载解析出来的Bean信息

AbstractBeanDefinition UML

AbstractBeanDefinition

AbstractBeanDefinition Field

Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第17张图片

1、属性总览

一个bean配置的所有信息,都会存在该AbstractBeanDefinition类中
Spring源码解析之一---将Dom实例解析成BeanDefinition对象_第18张图片
在了解Xml中的配置是怎样被封装到BeanDefinition中时,首先看个Xml配置的例子,该例子尽量涵盖所有情况

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
       
       default-lazy-init="default/true/false" default-merge="default/true/false"
       default-autowire="default/byName/byType/constructor/no" default-autowire-candidates="bean1,bean2"
       default-init-method="defaultInit" default-destroy-method="defaultDestroy">

    <bean id="beanA" class="com.example.demo.循环依赖.BeanA">
        <property name="beanB

你可能感兴趣的:(IOC,spring,java)