1.写在前面
前面的博客,笔者介绍spring中创建Bean的过程,但是还是没有讲完,只讲了如何将这个Bean给实例化出来,但是有关这个bean的所有的内容、属性等等东西,都还没有填充,由于将后面的创建的Bean的流程,需要了解合并BeanDefinition的原理,所以今天笔者打算讲下合并BeanDefinition的原理讲下。
2.什么是合并BeanDefinition
我们先来看个例子,具体的代码如下:
package com.ys.mergeBeanDefinition;
public class RootBean {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "RootBean{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
package com.ys.mergeBeanDefinition;
public class ChildBean {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "ChildBean{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
package com.ys.mergeBeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition rootBean = new RootBeanDefinition();
rootBean.setBeanClass(RootBean.class);
rootBean.getPropertyValues().add("name","张三");
rootBean.getPropertyValues().add("age","18");
context.registerBeanDefinition("root", rootBean);
GenericBeanDefinition childBean = new GenericBeanDefinition();
childBean.setBeanClass(ChildBean.class);
childBean.getPropertyValues().add("name", "李四");
childBean.setParentName("root");
context.registerBeanDefinition("child", childBean);
context.refresh();
System.out.println(context.getBean(ChildBean.class));
}
}
运行的结果如下:
可以看到笔者没有给childBean设置age的属性,但是这儿打印age的是18,笔者只在这儿设置了一个parentName
属性,spring就帮我们进行合并了,所以我们在创建Bean的时候,实际上用来创建的Bean的BeanDefinition
都是经过Spring进行合并后的BeanDefinition
,因为这儿如果创建ChildBean
采用的它没有合并过的BeanDefinition
的话,这儿创建出来的ChildBean
中age的属性应该是空的。
3.如何合并BeanDefinition
那么在什么时候合并BeanDefinition
呢?合并BeanDefinition
肯定在创建Bean之前,而且创建BeanDefinition
之前,一定要看这个parentName
的值是不是空,如果是空就不合并,如果不是空就合并。于是笔者在GenericBeanDefinition
的getParentName
的方法加了一个断点,然后调试这个方法,看对应的调用栈如下:
可以看到我们第一次的合并在调用getBeanNamesForType
的方法的时候,就开始合并了。然后调用的是doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
在这个方法中调用了getMergedLocalBeanDefinition(beanName);
进行对应的合并。于是笔者就直接看这部分的代码,具体的代码如下:
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 && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
于是笔者在这儿加了一个条件断点,先查看一下rootBean的情况,再查看childBean的情况,第一次从mergedBeanDefinitions
这个变量中取值,肯定是没有值,只有合并过后,这个mergedBeanDefinitions
中才有值,于是会调用getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
方法,具体的代码如下:
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
return getMergedBeanDefinition(beanName, bd, null);
}
发现继续调用的是getMergedBeanDefinition(beanName, bd, null);
的方法,具体的代码如下:
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
RootBeanDefinition previous = 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 || mbd.stale) {
previous = mbd;
//判断这个BeanDefinition的parentName值是不是等于null,由于我们这儿是root,所有这儿是空的
if (bd.getParentName() == null) {
// Use copy of given root bean definition.
//判断这个BeanDefinition的类型是不是RootBeanDefinition的,这儿root是的
if (bd instanceof RootBeanDefinition) {
//克隆这个BeanDefinition
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
//若不是也是直接将这个BeanDefinition变成RootBeanDefinition
mbd = new RootBeanDefinition(bd);
}
}
//这个就是需要的合并的,我们下面会讲
else {
// Child bean definition: needs to be merged with parent.
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 {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without a ConfigurableBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// Deep copy with overridden values.
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);
}
// Set default singleton scope, if not configured before.
// 若之前没有配置,直接设置为单例模式
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(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.
// 非单例bean中包含的bean本身不能是单例。
// 让我们在这里即时纠正 因为这可能是外层bean的父子合并的结果
// 在这种情况下,原始的内部bean定义将不会继承合并的外部bean的单例状态。
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)
// 暂时缓存合并的bean定义
//(稍后可能仍会重新合并以获取元数据更改)
// 存到合并的bd的map中去。
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}
上面的代码就是如果没有设置parentName
的属性的话,如果这个BeanDefinition
的类型是RootBeanDefinition
直接克隆一遍,如果这个BeanDefinition
的类型不是RootBeanDefinition
的类型的话,就直接创建RootBeanDefinition
的对象,将这个BeanDefinition
赋值给RootBeanDefinition
,最后再设置对应的作用域,同时将这个BeanDefinition
存到mergedBeanDefinitions
的map中去。上面笔者带着读者看了下,不需要合并的BeanDefinition
的类型,下面就看看需要合并的BeanDefinition
,还是看上面的代码,具体的代码如下:
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
RootBeanDefinition previous = 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 || mbd.stale) {
previous = mbd;
//判断这个BeanDefinition的parentName值是不是等于null,由于我们这儿是child,所有这儿不是空的
if (bd.getParentName() == null) {
// Use copy of given root bean definition.
//判断这个BeanDefinition的类型是不是RootBeanDefinition的,这儿root是的
if (bd instanceof RootBeanDefinition) {
//克隆这个BeanDefinition
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
//若不是也是直接将这个BeanDefinition变成RootBeanDefinition
mbd = new RootBeanDefinition(bd);
}
}
//这个时候需要合并
else {
// Child bean definition: needs to be merged with parent.
BeanDefinition pbd;
try {
//取出原始的名称,可能有别名
String parentBeanName = transformedBeanName(bd.getParentName());
//判断两个名称是否是一样的,
if (!beanName.equals(parentBeanName)) {
//又调用了一次合并,等于是递归的调用,因为这个parentName的BeanDefinition的parentName的值也不等于空,
//直到找到等于null的,就不合并了,由于我们的parentName的值是root,所有不需要合并,这儿返回的就是root表示的BeanDefinition
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
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 a ConfigurableBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// Deep copy with overridden values.
//将父类的值赋值给通过构造函数创建成RootBeanDefinition赋值给mbd
mbd = new RootBeanDefinition(pbd);
//然后将子类特有值进行覆盖
mbd.overrideFrom(bd);
}
// Set default singleton scope, if not configured before.
// 若之前没有配置,直接设置为单例模式
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(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.
// 非单例bean中包含的bean本身不能是单例。
// 让我们在这里即时纠正 因为这可能是外层bean的父子合并的结果
// 在这种情况下,原始的内部bean定义将不会继承合并的外部bean的单例状态。
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)
// 暂时缓存合并的bean定义
//(稍后可能仍会重新合并以获取元数据更改)
// 存到合并的bd的map中去。
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}
上面的代码就是递归调用一直合并下去,直到parentName
的值为空的时候,这个时候找到对应BeanDefinition
,将对应的父的BeanDefinition
直接创建成RootBeanDefinition
,然后再通过调用mbd.overrideFrom(bd);
方法,将子类特有的值进行覆盖,我们来看下覆盖的规则,具体的代码如下:
public void overrideFrom(BeanDefinition other) {
//如有直接覆盖BeanClassName
if (StringUtils.hasLength(other.getBeanClassName())) {
setBeanClassName(other.getBeanClassName());
}
//如有作用域直接覆盖作用域
if (StringUtils.hasLength(other.getScope())) {
setScope(other.getScope());
}
//覆盖是否抽象
setAbstract(other.isAbstract());
//如有直接覆盖工厂Bean的姓名
if (StringUtils.hasLength(other.getFactoryBeanName())) {
setFactoryBeanName(other.getFactoryBeanName());
}
//如有直接覆盖工厂方法名
if (StringUtils.hasLength(other.getFactoryMethodName())) {
setFactoryMethodName(other.getFactoryMethodName());
}
//设置Role
setRole(other.getRole());
//设置源
setSource(other.getSource());
copyAttributesFrom(other);
//如果不是自己实现的BeanDefinition的话,都是继承这个BeanDefinition的
if (other instanceof AbstractBeanDefinition) {
AbstractBeanDefinition otherAbd = (AbstractBeanDefinition) other;
//如有BeanClass直接覆盖
if (otherAbd.hasBeanClass()) {
setBeanClass(otherAbd.getBeanClass());
}
//如有构造函数的参数的直接覆盖
if (otherAbd.hasConstructorArgumentValues()) {
getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues());
}
//如有属性的参数直接覆盖
if (otherAbd.hasPropertyValues()) {
getPropertyValues().addPropertyValues(other.getPropertyValues());
}
//如果有方法重写直接覆盖
if (otherAbd.hasMethodOverrides()) {
getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
}
//设置过懒加载直接覆盖
Boolean lazyInit = otherAbd.getLazyInit();
if (lazyInit != null) {
setLazyInit(lazyInit);
}
//设置自动装配的模型
setAutowireMode(otherAbd.getAutowireMode());
//设置依赖检查
setDependencyCheck(otherAbd.getDependencyCheck());
//设置dependsOn
setDependsOn(otherAbd.getDependsOn());
//设置自动装配的候选对象
setAutowireCandidate(otherAbd.isAutowireCandidate());
//Primary注解
setPrimary(otherAbd.isPrimary());
copyQualifiersFrom(otherAbd);
//这两个属性在创建Bean的实例的时候有讲到
setInstanceSupplier(otherAbd.getInstanceSupplier());
setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed());
setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
//如有初始化方法,直接设置
if (otherAbd.getInitMethodName() != null) {
setInitMethodName(otherAbd.getInitMethodName());
setEnforceInitMethod(otherAbd.isEnforceInitMethod());
}
//如有销毁方法,直接设置
if (otherAbd.getDestroyMethodName() != null) {
setDestroyMethodName(otherAbd.getDestroyMethodName());
setEnforceDestroyMethod(otherAbd.isEnforceDestroyMethod());
}
//设置是否是合成的
setSynthetic(otherAbd.isSynthetic());
setResource(otherAbd.getResource());
}
else {
getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues());
getPropertyValues().addPropertyValues(other.getPropertyValues());
setLazyInit(other.isLazyInit());
setResourceDescription(other.getResourceDescription());
}
}
上面的规则,大概就是将子类特有的属性进行赋值到父类的BeanDefinition中去,这就是合并的原理,但是读者可能有一个问题了。我们这儿是先注册的是root,这个时候从mergedBeanDefinitions
中取值,这个时候能够取到这个BeanDefinition
,如果我们先注册的child的话,这个去root是不是取不到,那怎么办?可能刚才笔者没有细讲,读者可以看如下的代码:
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 && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
可以看到如果这个时候如果从mergedBeanDefinitions
取出来的值是空的,这个时候会执行getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
,注意看这个方法中调用了getBeanDefinition(beanName)
,然后我们再来看getBeanDefinition(beanName)
代码,具体的代码如下:
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (bd == null) {
if (logger.isTraceEnabled()) {
logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
}
return bd;
}
可以发现这个getBeanDefinition()
是从beanDefinitionMap
中拿,这个里面是百分之百有值的。所以这个问题解决了。记住这儿是在调用Bean工厂的后置处理器之前合并BeanDefinition
的,那么可能有读者会问,既然后面可以调用Bean工厂的后置处理器来对BeanDefinition
的信息进行修改,那么这儿为什么要进行合并?直接等到创建Bean的实例之前再和平就好了,因为这儿合并一次后,后面如果BeanDefinition
的信息修改了,那么后面又要合并一次,不是这次的合并是没有意义的吗?其实不然,因为这儿的合并并不是作用普通的BeanDefinition
,因为spring有些Bean是开天辟地的,用来扫描的什么的?这些BeanDefinition
不会经过对应的Bean工厂的后置处理器修改,因为这些类在是在调用Bean工厂的后置器之前就创建了,所以防止一部分的扩展中用到了parentName,所以在这儿就进行了一次合并。并不冲突,正常的情况下,我们这儿的合并不是在这儿调用,而是在我们扫描出来BeanDefinition
后再进行合并的。
4.重新合并BeanDefinition
通过看bean工厂后置处理器的执行流程的方法(invokeBeanFactoryPostProcessors()
)发现在执行完Bean工厂的后置处理器后并没有进行对应的合并,也就是说在其他的地方肯定有进行了一次合并,因为执行完Bean工厂的后置处理器后,可能会对BeanDefinition
的信息进行修改过,所以肯定要重新合并,那么重新合并的代码在哪里呢?这个时候我们只需要用idea工具查找mergedBeanDefinitions
的所有的使用地方,找到对应的地方,打上对应的断点,然后看对应的调用栈,具体的如下:
可以看到上面的注释,是重新合并一次BeanDefinition,具体的调用链如下:
org.springframework.context.support.AbstractApplicationContext#refresh
---> org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
---> org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
---> org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
---> org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
---> org.springframework.beans.factory.support.AbstractBeanFactory#markBeanAsCreated
可以看到这个markBeanAsCreated
的方法的调用是在创建Bean的实例之前,markBeanAsCreated
的代码如下:
protected void markBeanAsCreated(String beanName) {
//判断这个这个Bean是否已经创建,很明显这儿没有创建
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
//双重检查
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
clearMergedBeanDefinition(beanName);
this.alreadyCreated.add(beanName);
}
}
}
}
上面是调用clearMergedBeanDefinition(beanName);
方法来清楚原来的已经合并好的BeanDefinition
,具体的代码如下:
@Override
protected void clearMergedBeanDefinition(String beanName) {
super.clearMergedBeanDefinition(beanName);
this.mergedBeanDefinitionHolders.remove(beanName);
}
protected void clearMergedBeanDefinition(String beanName) {
RootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
//将这个状态设置为true。这个标识的字段只有在false的时候才不会合并,如果是true的话就会合并。
if (bd != null) {
bd.stale = true;
}
}
上面改了一个字段state,这个字段只有在false的时候才不会合并,如果是true时候就会合并,我们可以再次重温一下合并的代码,具体的代码如下:
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 && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
至此整个合并的流程就讲完了。
5.写在最后
这篇博客主要讲了合并BeanDefinition
的原理,下篇博客我们继续讲Spring创建Bean的流程。