spring之Bean的生命周期之BeanDefinition的合并解析

  • Post author:
  • Post category:其他




概述

开发人员写的类被spring扫描到容器后,并不是一开始就是一个完整的Bean了,spring扫描的是被注解或者是xml配置的类的信息,spring将类的信息或者是xml的信息保存在BeanDefinition的数据结构中,然而,如果我们想要得到一个可以使用的对象,是需要由spring来创建,这也就是网上常说的将bean的管理和创建交给spring

由于bean的生命十分复杂,涉及的点也比较多,本文主要对BeanDefintion的合并进行解析,而关于生命周期的后面的内容会陆续跟新



生命周期

首先简单看一下生命周期前的方法调用栈

在这里插入图片描述
从方法名字就可以看出核心的方法preInstantiateSingletons,在这里面就会完成spring 的单例Bean的初始化

接下来直接进入preInstantiateSingletons

在preInstantiateSingletons这个方法里面有两个for循环,我们来看第一个

List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
		for (String beanName : beanNames) { 
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

首先我们注意到的是,现在spring中去获取了一个List集合,那么这个集合是什么呢???

前面说到过spring在扫描对应包下的类后,会读取类的信息然后存储到BeanDefinition这个数据结构中

这里获取到的正是这个数据结构的集合,接下来的操作就应该很容易到了,即然已经获取到了类的元数据集合

那么我们直接循环遍历,根据元数据创建对象就好了,而spring 也真是这样做的

但是循环的第一步却没有直接做更创建对象直接相关的事情,这里调用了getMergedLocalBeanDefinition(beanName)

这个方法的作用是对于BeanDefinition的合并,下面先进入这个方法

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null && !mbd.stale) {
			return mbd;
		}
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

Merged的意思是合并,这里是在合并BeanDefinition,那么什么情况要进行BeanDefinition的合并呢?

<bean name="myBean" abstract="true" class="com.service.myBean"/>

<bean name="myBeanImpl" class="com.service.myBeanImpl" parent="myBean">
</bean>

有关于涉及Bean的合并一般而言是正对xml的配置,通过上面的xml配置可以观察到一个parent的属性

这个属性指向myBean,就可以理解为myBean是myBeanImpl的parent,这里跟java的继承有点像

类似于子类会继承父类的一些特性,同时子类也能覆盖父类的特性,这里也是一样的

例如:如果我们在myBean的标签里面添加上 scope=“prototype” ,那么对于myBeanImpl而言,它的scope=“prototype”

至于这个属性到底是怎么继承过来的呢?就是通过这里的getMergedLocalBeanDefinition方法

此外还有一点也需要注意我们可以看到在preInstantiateSingletons这个方法中返回的是RootBeanDefinition

这是BeanDefintion的一个子类,它和别的BeanDefintion的有什么区别么?

答案是有的,我们通过xml扫描进入到spring容器中,会将xml中的信息解析然后封装GenericBeanDefinition中

这个类也是一个子类但是于rootBeanDefinition相比,他们的不同如下

rootBeanDefiniton
@Override
	public String getParentName() {return null;}
	@Override
	public void setParentName(@Nullable String parentName) {
		if (parentName != null) {
			throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
		}
	}
GenericBeanDefinition
@Override
	public void setParentName(@Nullable String parentName) {this.parentName = parentName;}
	@Override
	@Nullable
	public String getParentName() {return this.parentName;}

不论是GenericBeanDefinition还是rootBeanDefintion都是BeanDefinition的子类,他们的功能大同小异

而其中主要的区别也很容易看出来

对于rootBeanDefiniton的Parent属性的get/set方法,一个是返回null,另外一个是抛出异常

而对于GenericBeanDefinition的parent的get/set方法则是普通的get/set

这样做的目的其实是不允许RootBeanDefintion拥有父类,表示最顶级的,不需要合并的类,或者表示为已经被合并完毕的类

而getMergedLocalBeanDefinition这个方法的目的也非常明显了,就是为了找到合并完之后的BeanDefinition了

在这里我认为面向接口编程的好处就能体现出来了!!!

1.灵活性高,不论是rootBeanDefinition还是genericBeanDefinition都是BeanDefinition的子类

所以在使用上可以根据需要灵活切换

2.代码复用性高,只是根据不同的特点,修改部分方法实现就可以了

3.同时在我认为rootBeanDefinition和genericBeanDefinition他们之间不是没有关系的,他们有共同的父类,

但是彼此之间又不会有影响,这样的代码其实跟容易给人模块化的感觉,充分发挥面向对象编程的优势



如何进行合并

接下我们来研究一下BeanDefinition具体是怎么进行合并的呢?

protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			RootBeanDefinition previous = null;
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}
			if (mbd == null || mbd.stale) {
				previous = mbd;
				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)) {
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {//异常代码省略}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {//异常代码省略}
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);	
				}
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}
				if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

最终进行合并的是上面的这个方法,先介绍参数

参数1:这里传入过来的是我们当前正要创建Bean的BeanName

参数2:传入的是我们当前正要创建Bean的BeanDefinition

参数3:在这里是直接传入了一个null

具体实现也是比较简单

1.spring先从缓存里面拿一下,看看之前有没有这个BeanName合并过的BeanDefinition,如果有,就先创建一个rootBeanDefinition

的对象,然后直接返回这个root就好了

2.如果没有就要进入合并的步骤了

代码摘自上面的 getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
if (bd.getParentName() == null) {
	if (bd instanceof RootBeanDefinition) {
	mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}else {
	mbd = new RootBeanDefinition(bd);
}

在这个if会先判断当前这个BeanDefiniton有没有parent,如果没有需要将当前BeanDefinition的内容拷贝到新创建的RootBeanfinition里面,然后返回出去即可

如果说有parent进入下面

else {
					BeanDefinition pbd;
					try {
						String parentBeanName = transformedBeanName(bd.getParentName());
						if (!beanName.equals(parentBeanName)) {
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {//异常代码省略}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {//异常代码省略}
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);	
				}
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}
				if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}
			}

这里的代码也不难也很容易阅读,主要做的也就是,获取到当前BeanDefintion的parent,然后将当前bd的内容覆盖parent的内容,然后放入一个rootBeanDefintion进行返,所以基于这个特性我们可以总结到,这里子BeanDefinition是可以覆盖父Beanfinition的属性,也就是说,如果子Beanfinition和父Beanfinition都有A属性,那么后面的生命周期中会以子Beanfinition的A属性为准

除此之外这里还有两点需要注意一下:

1.pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);

源码里面有一步递归的操作,这个是为了解决,父BeanDefintion还可能有parent

2.如果当前容器里面没有找到parent,spring还会进入父容器里面去找,如果在父容器里面找到了

也会进行合并,这里不过多介绍父子容器相关的概念

以上就是BeanDefintion的全部内容,进行BeanDefintion合并完成后,就可以进行生命周期的下一步了(后续更新)



版权声明:本文为kznsbs原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。