mergeBeanDefinition原理

本文探讨了Spring框架中mergeBeanDefinition的原理,分析了如何处理rootBeanDefinition和childBeanDefinition的关系,以及在现代源码中如何处理ScannedGenericBeanDefinition。通过示例代码展示了合并过程,并总结了mergeBeanDefinition在初始化beanDefinition时的作用,以及其在兼容性和性能优化上的考量。

介绍

在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来设置属性的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值