什么是BeanDefinition
BeanDefination
包含了我们对bean做的配置,比如XML<bean/>
标签的形式进行配置;- Spring将我们对bean的定义信息进行了抽象,抽象后的实体就是
BeanDefinition
,并且Spring会以此作为标准来对Bean进行创建; BeanDefinition
包含以下元数据:- 一个全限定类名,通常来说,就是对应的bean的全限定类名;
- bean的行为配置元素,这些元素展示了这个bean在容器中是如何工作的,包括
scope
lifecycle callbacks
; - 这个bean的依赖信息;
- 一些其他配置,比如连接池对象的最大连接数等;
Java正常创建Bean对象和spring创建对象的区别
- Java正常创建Bean对象
- spring通过
BeanDefinition
来创建bean:
相比于正常的创建过程,spring对其管理的bean没有直接采用new 的方式,而是先通过解析配置数据以及根据对象本身的一些定义而获取其对应的
BeanDefinition
,并将这个BeanDefinition
作为之后创建这个bean的依据,同时Spring在这个过程中提供了一些扩展点,例如BeanFactoryProcessor
。
BeanDefinition
的方法分析:
// 获取父BeanDefinition,主要用于合并,下节中会详细分析
String getParentName();
// 对于的bean的ClassName
void setBeanClassName(@Nullable String beanClassName);
// Bean的作用域,不考虑web容器,主要两种,单例/原型,见官网中1.5内容
void setScope(@Nullable String scope);
// 是否进行懒加载
void setLazyInit(boolean lazyInit);
// 是否需要等待指定的bean创建完之后再创建
void setDependsOn(@Nullable String... dependsOn);
// 是否作为自动注入的候选对象
void setAutowireCandidate(boolean autowireCandidate);
// 是否作为主选的bean
void setPrimary(boolean primary);
// 创建这个bean的类的名称
void setFactoryBeanName(@Nullable String factoryBeanName);
// 创建这个bean的方法的名称
void setFactoryMethodName(@Nullable String factoryMethodName);
// 构造函数的参数
ConstructorArgumentValues getConstructorArgumentValues();
// setter方法的参数
MutablePropertyValues getPropertyValues();
// 生命周期回调方法,在bean完成属性注入后调用
void setInitMethodName(@Nullable String initMethodName);
// 生命周期回调方法,在bean被销毁时调用
void setDestroyMethodName(@Nullable String destroyMethodName);
// Spring可以对bd设置不同的角色,了解即可,不重要
// 用户定义 int ROLE_APPLICATION = 0;
// 某些复杂的配置 int ROLE_SUPPORT = 1;
// 完全内部使用 int ROLE_INFRASTRUCTURE = 2;
void setRole(int role);
// bean的描述,没有什么实际含义
void setDescription(@Nullable String description);
// 根据scope判断是否是单例
boolean isSingleton();
// 根据scope判断是否是原型
boolean isPrototype();
// 跟合并beanDefinition相关,如果是abstract,说明会被作为一个父beanDefinition,不用提供class属性
boolean isAbstract();
// bean的源描述,没有什么实际含义
String getResourceDescription();
// cglib代理前的BeanDefinition
BeanDefinition getOriginatingBeanDefinition();
BeanDefinition
的继承关系
1. BeanDefinition
继承的接口:
org.springframework.core.AttributeAccessor
这个接口为从其他任意类中获取或设置元数据提供了一个通用的规范;将数据接口和操作方法进行分离,提供一个获取属性跟设置属性的方法。
void setAttribute(String name, @Nullable Object value);
Object getAttribute(String name);
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
org.springframework.beans.BeanMetadataElement
这个接口提供一个方法去获取配置源对象,其实就是源文件。
@Nullable
Object getSource();
可以理解为,当我们通过注解的方式定义了一个IndexService
对象时,此时的IndexService
对应的BeanDefinition
通过getSource
方法返回的就是IndexService.class
这个文件对应的一个File对象。
2. AbstractBeanDefinition
org.springframework.core.AttributeAccessorSupport
:AttributeAccessor
将数据结构跟操作方法分离,数据结构就在AttributeAccessorSupport
中。
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
@Override
public void setAttribute(String name, @Nullable Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
}
else {
removeAttribute(name);
}
}
......省略下面的代码
这个类中,维护了一个map,这就是BeanDefinition
体系中,通过访问者模式所有操作的数据对象;
org.springframework.beans.BeanMetadataAttributeAccessor
:这个类主要是对上面的map中的数据操作做了更深一层的封装:
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
public BeanMetadataAttribute getMetadataAttribute(String name) {
return (BeanMetadataAttribute) super.getAttribute(name);
}
AbstractBeanDefinition
通过继承了BeanMetadataAttributeAccessor
,可以对BeanDefinition
中的属性进行操作,这里属性仅仅代表BeanDefinition
中的一个map。
为什么需要AbstractBeanDefinition?
AbstractBeanDefinition
对BeanDefinition
的大部分方法做了实现,同时定义了一系列常用及默认字段,这是因为BeanDefinition
接口过于顶层,如果我们依赖BeanDefinition
这个接口直接去创建实现类的话过于麻烦,所以通过AbstractBeanDefinition
做了一个下沉,并给很多属性赋了默认值:
// 默认情况不是懒加载的
private boolean lazyInit = false;
// 默认情况不采用自动注入
private int autowireMode = AUTOWIRE_NO;
// 默认情况作为自动注入的候选bean
private boolean autowireCandidate = true;
// 默认情况不作为优先使用的bean
private boolean primary = false;
........
3. AbstractBeanDefinition
的三个子类
GenericBeanDefinition
- 替代原来的
ChildBeanDefinition
,比起ChildBeanDefinition
更为灵活,ChildBeanDefinition
在实例化的时候必须要指定一个parentName
而GenericBeanDefinition
不需要,通过注解配置的bean以及我们的配置类的BeanDefinition
类型都是GenericBeanDefinition
。
ChildBeanDefinition
已经被GenericBeanDefinition
替代;
RootBeanDefination
- Spring在启动时都会实例化几个初始化的
BeanDefinition
,这几个BeanDefinition
的类型都是RootBeanDefinition
。
4.AnnotatedBeanDefinition
这个接口继承BeanDefinition
接口:
AnnotationMetadata getMetadata();
@Nullable
MethodMetadata getFactoryMethodMetadata();
相较于BeanDefinition
,仅仅多提供了两个方法,
-
getMetadata()
:主要用于获取注解元数据,从接口的命名上看出,这个类主要用于保存通过注解方式定义的bean所对应的BeanDefinition
,所以它多提供了一个关于获取注解信息的方法。 -
getFactoryMethodMetadata()
:这个方法跟我们的@Bean
注解相关,当一个方法被@Bean
注解时,就被解析成FactoryMethodMetadata
。
5. AnnotatedBeanDefinition
的三个实现类
AnnotatedGenericBeanDefinition
- 通过形如下面API注册的bean都是
AnnotatedGenericBeanDefinition
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Config.class);
}
这里的config
对象,最后在spring容器终究是一个AnnotatedGenericBeanDefinition
;
- 通过
@Import
注解导入的类,最后都被解析为AnnotatedGenericBeanDefinition
;
ScannedGenericBeanDefinition
- 通过注解扫描的类,如
@Service
、@Compent
等方式配置的Bean都是ScannedGenericBeanDefinition
;
ConfigurationClassBeanDefinition
- 通过
@Bean
方式配置的Bean为ConfigurationClassBeanDefinition
。
6. BeanDefinition
的具体使用的子类
什么是BeanDefinition
的合并
一个
BeanDefinition
包含了很多的配置信息,包括构造参数,setter方法的参数还有容器特定的一些配置信息,比如初始化方法,静态工厂方法等,一个子BeanDefinition
可以从它的父BeanDefinition
继承配置信息,不仅如此,还可以覆盖其中的一些值或添加一些自己需要的属性,使用BeanDefinition
的父子定义可以减少很多的重复属性设置,父BeanDefinition
可以作为定义BeanDefinition
的模板。
子BeanDefinition
会从父BeanDefinition
中继承自身没有的属性,这个过程中,子BeanDefinition
中已经存在的属性不会被父BeanDefinition
中所覆盖。
注意
-
子
BeanDefinition
中的class属性如果为null,同时父BeanDefinition
又指定了class属性,那么子BeanDefinition
也会继承这个class属性; -
子
BeanDefinition
必须要兼容父BeanDefinition
中的所有属性; -
关于
BeanDefinition
中abstract
属性的说明:-
并不是作为父
BeanDefinition
就一定要设置abstract
属性为true,abstract
只代表这个BeanDefinition
是否要被spring实例化并创建对应的Bean,如果为true,代表容器不需要去对其进行实例化; -
如果一个
BeanDefinition
被当做父BeanDefinition
使用,并且没有指定其class,那么必须指定其abstract
为true。 -
abstact=true
一般会跟父BeanDefinition
一起使用,因为当我们设置某个BeanDefinition
的abstract=true
时,一般都是要将其当做BeanDefinition
的模板使用,否则这个BeanDefinition
也没有意义,除非我们使用其他BeanDefinition
来继承它的属性。
-
spring在哪些阶段做了合并
1. 扫描并获取到BeanDefinition
主要发生在invokeBeanFactoryPostProcessors
阶段;
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory,
List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// .....
// 省略部分代码,省略的代码主要时用来执行程序员手动调用API注册的容器的后置处理器
// .....
// 发生一次bd的合并
// 这里只会获取实现了BeanDefinitionRegistryPostProcessor接口的Bean的名字
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// 筛选实现了PriorityOrdered接口的后置处理器
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 去重
processedBeans.add(ppName);
}
}
// .....
// 只存在一个internalConfigurationAnnotationProcessor 处理器,用于扫描
// 这里只会执行了实现了PriorityOrdered跟BeanDefinitionRegistryPostProcessor的后置处理器
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// .....
// 这里又进行了一个bd的合并
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
// 筛选实现了Ordered接口的后置处理器
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// .....
// 执行的是实现了BeanDefinitionRegistryPostProcessor接口跟Ordered接口的后置处理器
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
boolean reiterate = true;
while (reiterate) {
reiterate = false;
// 这里再次进行了一次bd的合并
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
// 筛选只实现了BeanDefinitionRegistryPostProcessor的后置处理器
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 执行的是普通的后置处理器,即没有实现任何排序接口(PriorityOrdered或Ordered)
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// .....
// 省略部分代码,这部分代码跟BeanfactoryPostProcessor接口相关,这节bd的合并无关,下节容器的扩展点中我会介绍
// .....
}
每次调用beanFactory.getBeanNamesForType
都进行一次BeanDefinition
合并,getBeanNamesForType
这个方法主要目的是为了指定类型的BeanDefinition
的名称,之后通过BeanDefinition
的名称去找指定的BeanDefinition
,然后获取对应Bean。
2. 实例化
spring在实例化一个对象时也会进行BeanDefinition
合并。
第一次:
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
public void preInstantiateSingletons() throws BeansException {
// .....
// 省略跟合并无关的代码
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// .....
第二次:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// .....
// 省略跟合并无关的代码
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// ....
}
if (mbd.isSingleton()) {
// ....
}
// ....
为什么要合并
子
BeanDefinition
从父定义继承配置数据。子定义可以覆盖某些值或根据需要添加其他值。使用父BeanDefinition
和子BeanDefinition
定义可以节省很多输入。实际上,这是一种模板形式。父BeanDefinition
不能单独实例化,因为它不完整,并且还被明确标记为抽象。当定义是抽象的时,它只能用作纯模板BeanDefinition
,用作子定义的父定义。
在扫描阶段,之所以发生合并,是因为spring需要拿到指定了实现了BeanDefinitionRegistryPostProcessor
接口的BeanDefinition
的名称,也就是说spring需要用到BeanDefinition
中的一系列属性做判断,所以进行了一次合并,spring需要用到BeanDefinition
的属性,要保证获取到的BeanDefinition
的属性是正确的。
为什么获取到的BeanDefinition
中属性可能不正确?
- 作为子
BeanDefinition
,属性本身就有可能缺失; - spring提供很多扩展点,在容器启动的时候,可能会修改
BeanDefinition
中的属性,
总结
之所以在每次需要用到BeanDefinition
时都进行一次合并,是为了每次都拿到最新的,最有效的BeanDefinition
,因为容器提供了一些扩展点我们可以修改BeanDefinition
的属性。