介绍
在spring源码中,beanDefinition是一个最为重要的知识点之一,在源码中,会有一个merge的操作,将beanDefinition进行合并
org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition
在说mergeBeanDefinition之前,有一个概念,要先说清楚,就是在早期的版本中,有rootBeanDefinition和childBeanDefinition的概念,简单来说,就是一个beanDefinition的属性可以被继承,比如,一些公共的数据,可以放到rootBeanDefinition中,然后子beanDefinition直接继承rootBeanDefinition即可,多个子BeanDefinition无需多次在自己的beanDefinition设置公共属性,所以,我认为mergeBeanDefinition是为了兼容老版本的rootBeanDefinition,然后做的一个处理
在现在的源码中,通过自动扫描获取到的beanDefinition,是ScannedGenericBeanDefinition,也就是genericBeanDefinition的子类,那对于原来的rootBeanDefinition和childBeanDefinition是如何进行处理的呢?


应用
下面的这个例子,是我自己写的一个demo
@Configuration
@ComponentScan("com.spring.study.beandefinition")
public class BeanDefinitionConfig {
}
public class RootBeanEntity {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void testRoot() {
System.out.println("name:" + name + "\t" + "type:" + type);
}
}
public class UserEntity {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void test() {
System.out.println(name + "===" + type);
}
}
@Component
public class UserService {
}
public class BeanDefinitionTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(BeanDefinitionConfig.class);
/**
* 初始化一个rootBeanDefinition
*/
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.getPropertyValues().add("name","testName");
rootBeanDefinition.getPropertyValues().add("type","typeTest");
rootBeanDefinition.setBeanClass(RootBeanEntity.class);
ac.registerBeanDefinition("rootBeanEntity",rootBeanDefinition);
/**
* 将genericBeanDefinition的父bd指向上面的rootBeanDefinition
* 如果在genericBeanDefinition中覆盖了父bd中的属性,在打印的时候,就会取覆盖的值,如果未覆盖,则使用父bd的
*/
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(UserEntity.class);
genericBeanDefinition.getPropertyValues().add("name","genBeanName");
genericBeanDefinition.setParentName("rootBeanEntity");
ac.registerBeanDefinition("userEntity",genericBeanDefinition);
ac.refresh();
ac.getBean(RootBeanEntity.class).testRoot();
ac.getBean(UserEntity.class).test();
System.out.println("===============================");
System.out.println("通过@Configuration和@ComponentScan注入的bean是不同的beanDefinition类型");
/**
* class org.springframework.context.annotation.ScannedGenericBeanDefinition
* ScannedGenericBeanDefinition
* class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
* AnnotatedGenericBeanDefinition
*
* spring在扫描bean的时候,将包下所有的.class扫描到之后,会为每个class创建一个ScannedGenericBeanDefinition,所以,@Component注解对应
* 的bean都是Scanned类型的
*
* 加了@Configuration注解的bean,是在ac.registry()的时候,添加到beanDefinitionMap的,这时候,使用的是 AnnotatedBeanDefinitionReaders
* 所以,对于配置类是AnnotatedGenericBeanDefinition
*/
System.out.println(ac.getBeanDefinition("userService").getClass());
System.out.println(ac.getBeanDefinition("userService").getClass().getSimpleName());
System.out.println(ac.getBeanDefinition("beanDefinitionConfig").getClass());
System.out.println(ac.getBeanDefinition("beanDefinitionConfig").getClass().getSimpleName());
}
}
最终打印的结果是:
name:testName type:typeTest
genBeanName===typeTest
===============================
通过@Configuration和@ComponentScan注入的bean是不同的beanDefinition类型
class org.springframework.context.annotation.ScannedGenericBeanDefinition
ScannedGenericBeanDefinition
class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
AnnotatedGenericBeanDefinition
Process finished with exit code 0
源码
/**
* 这里大致的意思是:
* 1.在beanDefinition进行了合并之后,会存入到mergedBeanDefinitions这个集合中
* 2.如果从map中获取到了合并之后的bd,那就不需要再次进行解析了
* 3.如果获取不到,就重走一遍merge的步骤
* @param beanName
* @return
* @throws BeansException
*/
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// Check with full lock now in order to enforce the same merged instance.
//1.containingBd 这个参数从上层调用过来,在代码中写死的null;如果为null,就从map中判断一下,是否已经存在
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
/**
* 在5.2之后的源码中,这里的判断加了一个stale的判断,这个标识的意思是:定义是否需要合并bean
*
* 这里的mbd变量需要注意:
* 不管bd是RootBeanDefinition还是GenericBeanDefinition,都会强转成RootBeanDefinition(mbd)
*/
if (mbd == null) {
/**
* 2.判断当前bd是否有设置parentName
* 如果当前bd没有设置parentName,我们姑且可以认为,这个bd就是父bd
* 下面的判断可以看到:如果bd是RootBeanDefinition,就clone一个新的bd
* 如果bd不是RootBeanDefinition,那就根据bd,new一个RootBeanDefinition对象
*
*/
if (bd.getParentName() == null) {
// Use copy of given root bean definition.
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
// Child bean definition: needs to be merged with parent.
/**
* 3.如果进入到这里,说明当前bd设置了parentName属性
*/
BeanDefinition pbd;
try {
/**
* 3.1 这里的意思,我的理解是:递归获取到bd的parent,直到获取到最后一个bd
*/
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
/**
* 3.2 如果beanName和parentBeanName一样?这个场景是什么?
* 根据parentName获取到parentBeanDefinition
*/
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// Deep copy with overridden values.
/**
* 下面的两行代码中,
* 第一行代码是:根据父bd new一个RootBeanDefinition
* 第二行代码是:将bd(可以理解为子bd)的属性赋值到mbd中,子类的属性会覆盖父类的属性,
* 方法中,就是通过一堆的set进行属性覆盖
*
* 如果bd本身就是RootBeanDefinition,那就不会执行这里的逻辑
*/
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
// Set default singleton scope, if not configured before.
/**
* 如果bd没有设置scope,默认是singleton
*/
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}
// A bean contained in a non-singleton bean cannot be a singleton itself.
// Let's correct this on the fly here, since this might be the result of
// parent-child merging for the outer bean, in which case the original inner bean
// definition will not have inherited the merged outer bean's singleton status.
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}
// Cache the merged bean definition for the time being
// (it might still get re-merged later on in order to pick up metadata changes)
if (containingBd == null && isCacheBeanMetadata()) {
/**
* 将合并后的bd存入到map集合中,spring在后面初始化bean的流程中,就无需再次merge,直接从这个map中根据beanName获取即可
*/
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
return mbd;
}
}
这里的注释也加的比较清楚,就不做过多的解释了,大致可以分为两种情况
1、如果当前beanDefinition没有设置parent属性,那就是一个rootBeanDefinition,无需进行其他的处理
2、如果当前beanDefinition设置了parent属性,需要先将父beanDefinition的属性获取到,然后再将自己的beanDefinition的属性覆盖到父beanDefinition中
3、最后将得到的rootBeanDefinition存入到缓存中
总结
总结来说,
1.如果当前子beanDefinition有设置rootBeanDefinition,那在初始化子beanDefinition的时候,会合并rootBeanDefinition的属性,然后通过一些列的set方法,将子beanDefinition的属性覆盖rootBeanDefinition中的属性
2.如果当前beanDefinition没有设置parentBeanDefinition属性,那就表示当前beanDefinition本身就是rootBeanDefinition,那就只需要根据beanDefinition创建一个rootBeanDefinition,无需考虑属性覆盖的问题
3.在merge了之后,会将merge之后的beanDefinition放入到集合缓存中,spring源码中有多个位置会对beanDefinition进行merge操作,那放入到缓存之后,就无需每次去进行merge
4.所以我认为mergeBeanDefinition的这个操作,是为了兼容原来版本中rootBeanDefinition和childBeanDefinition这种设置方式,在我毕业工作之后,还没有遇到过项目是通过rootBeanDefinition和childBeanDefinition来设置属性的
本文探讨了Spring框架中mergeBeanDefinition的原理,分析了如何处理rootBeanDefinition和childBeanDefinition的关系,以及在现代源码中如何处理ScannedGenericBeanDefinition。通过示例代码展示了合并过程,并总结了mergeBeanDefinition在初始化beanDefinition时的作用,以及其在兼容性和性能优化上的考量。
943

被折叠的 条评论
为什么被折叠?



