XmlBeanFactory的uml图
首先了解下几个类的职能
BeanDefinitionRegistry: 定义Bean的常规操作,来注册BeanDefinition, 内部就是用一个 Map 实现.
SimpleBeanDefinitionRegistry 是 BeanDefinitionRegistry 一个简单的实现。只提供注册表的功能,不提供工厂其他功能。
DefaultListableBeanFactory:ConfigurableListableBeanFactory(其实就是 BeanFactory ) 和 BeanDefinitionRegistry 接口的默认实现:一个基于 BeanDefinition 元数据的完整 bean 工厂。
XmlBeanDefinitionReader:相当于是个统筹规划者
将Xml读取成Dom实例和将Dom实例注册到BeanDefinition都使用其他类
DefaultDocumentLoader:用于将Xml配置文件解析成Domcument实例
BeanDefinitionDocumentReader:用于将Document实例注册到BeanDefinitionRegistry
该类中有一个委托属性,即Dom实例中每个标签的解析委托给了delegate属性。
BeanDefinitionParserDelegate:用于解析 XML bean 定义的有状态委托类
总体需求:将Domcument实例解析成BeanDefinition对象并注册
1、需求划分及Spring的解决方案
1、解析根标签的默认属性
将beans标签的默认属性的解析委托给delegate属性;解析的默认属性存储在BeanDefinitionParserDelegate
类的beanDefinitionDefaults属性中。
首先回顾下beans标签中可填的属性有哪些
XSD风格的Xml文件以beans标签开头,其属性可以设置一些默认行为,例如
- default-lazy-init:
可选值 | 功能说明 |
---|---|
false(default) | spring在启动过程导致在启动时候,会默认加载整个对象实例图,比较耗时,故Spring项目启动都需要较长的时间 |
true | 不会加载整个对象实例图,启动速度较快,但生产环境一般设置为false |
- default-merge:较为少用,不作分析
- 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方式。 |
- default-autowire-candidates:告诉bean的容器,哪些可以做自动注入,哪些不可以,它支持按照bean的名称模糊匹配
- default-init-method: 指定所有bean的默认初始化方法
- default-destroy-method:指定所有bean的默认销毁方法
属性是如何填充的?
- 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>
- id和name属性
- id:id的属性值作为该bean的beanName
- name:name属性的值允许配置多个,用
,
或者;
分割;若id为空,则name值中第一个作为beanName,其余的作为该bean的别名
还需要检测该bean的beanName和别名在该容器中是否唯一
- 解析其他属性
- 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:同上
- meta子标签
遍历bean标签的所有子标签,对meta则进行处理;
<meta key="metaKey1" value="metaValue1"/>
<meta key="metaKey2" value="metaValue2"/>
BeanMetadataAttribute
- name:meta标签的key属性值
- value:meta标签的value属性值
- source:提取源,也就是标志该attributes属性从哪个Element实例提取的,实际为null
AttributeAccessorSupport
- attributes(
Map <String,BeanMetadataAttribute>
):其Map中一个Entry对应一个meta标签,Entry的key为name,value为BeanMetadataAttribute实例
-
description
<description>description1</description>
标签只能存在一个;其值(该例子中为description1)存储在description属性 -
方法重写—LookupOverride
MethodOverride:
- methodName:lookup-override标签的name属性值
- source:提取来源标签
LookupOverride
- beanName:方法返回的bean名称(该bean在容器中)
MethodOverrides:
- overrides:该属性位于AbstractBeanDefinition类中,用于存储MethodOverride集合。
- 方法重写—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方法的输出结果是:
// 我替换了原有的方法
MethodOverride:
- methodName:replaced-method标签的name属性值
- source:提取来源标签
ReplaceOverride
- methodReplacerBeanName:存储replaced-method标签中replacer的属性值
- constructor-arg
对于构造函数的存储;分为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标签的值被解析后是如何存放的
首先先总览一下解析值所涉及的类
接下来逐一详细分析,若无法理解可先跳过,先查看后面的方法解析
-
ref属性
RuntimeBeanReference
- toParent:默认为false,只有ref标签才会动用该属性,ref属性默认为false
- beanName:ref属性值
-
value属性:
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值
- 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以及赋值标签之间的关系,三者只能存一,因为都是给构造器某个参数注入值,只需要一个值就可。
- property
MutablePropertyValues
- propertyValueList:List集合
PropertyValue - name:property标签的name属性值
- value:property的值
该解析过程和constructor-arg类似,不再赘述
4、注册bean
2、重要的类解析
我们最终解析的结果都存放在该对象中,在此先对该对象做分析
1、解析的BeanDefinition所涉及的类
GenericBeanDefinition UML
GenericBeanDefinition用于承载解析出来的Bean信息
AbstractBeanDefinition UML
AbstractBeanDefinition Field
1、属性总览
一个bean配置的所有信息,都会存在该AbstractBeanDefinition类中
在了解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