BeanDefinition(二)

什么是合并?

一个BeanDefinition包含了很多的配置信息,包括构造参数,setter方法的参数还有容器特定的一些配置信息,比如初始化方法,静态工厂方法等等。一个子的BeanDefinition可以从它的父BeanDefinition继承配置信息,不仅如此,还可以覆盖其中的一些值或者添加一些自己需要的属性。使用BeanDefinition的父子定义可以减少很多的重复属性的设置,父BeanDefinition可以作为BeanDefinition定义的模板。

我们通过一个例子来观察下合并发生了什么,编写一个Demo如下:

<?xml version="1.0" encoding="UTF-8"?>
<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">
	<bean id="parent" abstract="true"
		  class="com.dmz.official.merge.TestBean">
		<property name="name" value="parent"/>
		<property name="age" value="1"/>
	</bean>
	<bean id="child"
		  class="com.dmz.official.merge.DerivedTestBean"
		  parent="parent" >
		<property name="name" value="override"/>
	</bean>
</beans>
public class DerivedTestBean {
	private String name;

	private int age;
	
    // 省略getter setter方法
}

public class TestBean {
	private String name;

	private String age;
    
     // 省略getter setter方法
}

public class Main {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application.xml");
		DerivedTestBean derivedTestBean = (DerivedTestBean) cc.getBean("child");
		System.out.println("derivedTestBean的name = " + derivedTestBean.getName());
		System.out.println("derivedTestBean的age = " + derivedTestBean.getAge());
	}
}

结果:

derivedTestBean的name = override
derivedTestBean的age = 1

在上面的例子中,我们将DerivedTestBean的parent属性设置为了parent,指向了我们的TestBean,同时将TestBean的age属性设置为1,但是我们在配置文件中并没有直接设置DerivedTestBean的age属性。但是在最后运行结果,我们可以发现,DerivedTestBean中的age属性已经有了值,并且为1,就是我们在其parent Bean(也就是TestBean)中设置的值。也就是说,子BeanDefinition会从父BeanDefinition中继承没有的属性。另外,DerivedTestBean跟TestBean都指定了name属性,但是可以发现,这个值并没有被覆盖掉,也就是说,子BeanDefinition中已经存在的属性不会被父BeanDefinition中所覆盖。

所以我们可以总结如下:

  • 子BeanDefinition会从父BeanDefinition中继承没有的属性

  • 这个过程中,子BeanDefinition中已经存在的属性不会被父BeanDefinition中所覆盖

关于合并需要注意的点:

  • 子BeanDefinition中的class属性如果为null,同时父BeanDefinition又指定了class属性,那么子BeanDefinition也会继承这个class属性。
  • 子BeanDefinition必须要兼容父BeanDefinition中的所有属性。这是什么意思呢?以我们上面的demo为例,我们在父BeanDefinition中指定了name跟age属性,但是如果子BeanDefinition中子提供了一个name的setter方法,这个时候Spring在启动的时候会报错。因为子BeanDefinition不能承接所有来自父BeanDefinition的属性
  • 关于BeanDefinition中abstract属性的说明:
    a. 并不是作为父BeanDefinition就一定要设置abstract属性为true,abstract只代表了这个BeanDefinition是否要被Spring进行实例化并被创建对应的Bean,如果为true,代表容器不需要去对其进行实例化。
    b. 如果一个BeanDefinition被当作父BeanDefinition使用,并且没有指定其class属性。那么必须要设置其abstract为true
    c. abstract=true一般会跟父BeanDefinition一起使用,因为当我们设置某个BeanDefinition的abstract=true时,一般都是要将其当作BeanDefinition的模板使用,否则这个BeanDefinition也没有意义,除非我们使用其它BeanDefinition来继承它的属性

Spring在哪些阶段做了合并?

扫描并获取到bd

这个阶段的操作主要发生在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的合并无关,下节容器的扩展点中我会介绍
        // .....
		
	}

在这里插入图片描述

实例化

Spring在实例化一个对象也会进行bd的合并。
第一次:
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()) {
        // ....
    }
    // ....

我们可以发现这两次合并有一个共同的特点,就是在合并之后立马利用了合并之后的bd我们简称为mbd做了一系列的判断,比如上面的dependsOn != null和mbd.isSingleton()。基于上面几个例子我们来分析:为什么需要合并?

为什么需要合并?

在扫描阶段,之所以发生了合并,是因为Spring需要拿到指定了实现了BeanDefinitionRegistryPostProcessor接口的bd的名称,也就是说,Spring需要用到bd的名称。所以进行了一次bd的合并。在实例化阶段,是因为Spring需要用到bd中的一系列属性做判断所以进行了一次合并。我们总结起来,其实就是一个原因:Spring需要用到bd的属性,要保证获取到的bd的属性是正确的。

那么问题来了,为什么获取到的bd中属性可能不正确呢?

主要两个原因:

  1. 作为子bd,属性本身就有可能缺失,比如我们在开头介绍的例子,子bd中本身就没有age属性,age属性在父bd中
  2. Spring提供了很多扩展点,在启动容器的时候,可能会修改bd中的属性。比如一个正常实现了BeanFactoryPostProcessor就能修改容器中的任意的bd的属性。

合并的代码分析:

因为合并的代码其实很简单,所以一并在这里分析了,也可以加深对合并的理解:

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    // 从缓存中获取合并后的bd
    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.
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			if (mbd == null) {
                // 如果没有parentName的话直接使用自身合并
                // 就是new了RootBeanDefinition然后再进行属性的拷贝
				if (bd.getParentName() == null) {
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {   
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// 需要进行父子的合并
					BeanDefinition pbd;
					try {
						String parentBeanName = transformedBeanName(bd.getParentName());
						if (!beanName.equals(parentBeanName)) {
                            // 这里是递归,在将父子合并时,需要确保父bd已经合并过了
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
                            // 一般不会进这个判断
                            // 到父容器中找对应的bean,然后进行合并,合并也发生在父容器中
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							// 省略异常信息......
						}
					}
					// 省略异常信息......
					// 
					mbd = new RootBeanDefinition(pbd);
                    //用子bd中的属性覆盖父bd中的属性
					mbd.overrideFrom(bd);
				}

				// 默认设置为单例
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
				}
                // 当前bd如果内部嵌套了一个bd,并且嵌套的bd不是单例的,但是当前的bd又是单例的
                // 那么将当前的bd的scope设置为嵌套bd的类型
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}
				// 将合并后的bd放入到mergedBeanDefinitions这个map中
                // 之后还是可能被清空的,因为bd可能被修改
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}

			return mbd;
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值