Spring循环依赖问题--内部是如何解决的?

问题描述

循环依赖其实就是两个类相互依赖,它们各自都需要去获取对方的实例,而导致的问题。

public class A {
    public A() {
    }

    private B b;

    public void setB(B b) {
        this.b = b;
    }
}

B对象

public class B {
    private A a;

    public B() {
    }

    public void setA(A a) {
        this.a = a;
    }
}

xml配置文件

<bean id="a" class="com.pri.spring5.demo.A">
    <property name="b" ref="b"></property>
</bean>

<bean id="b" class="com.pri.spring5.demo.B">
    <property name="a" ref="a"></property>
</bean>

如下图所示:
在这里插入图片描述

Spring是如何解决的

在这里插入图片描述

三级缓存

在这里插入图片描述

  • 一级缓存: 存放已经经过了完整的生命周期的bean;
  • 二级缓存: 存放需要早期曝光的bean,该bean是一个尚未初始化完成的半成品对象(一般只有处于循环引用状态的Bean才会被保存在该缓存中);
  • 三级缓存: 从源码中可以看到,三级缓存并不像一、二级缓存直接存放bean对象,而是存放一个ObjectFactory函数式接口对象,该接口只有一个方法,getObject,我们可以通过这个方法获取对应的bean。spring默认实例化bean之后,在下一步就会将该bean存入三级缓存中。

内部实现过程

流程图

基于上述的A、B案例,我这里画了一个详细的流程图。

刚开始看的话,图比源码更直观。

在这里插入图片描述

内部重点实现方法

先从doGetBean方法开始,在里面分别有两个getSingleton方法,注意,这两个方法不是同一个方法!!而且很重要!!

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

   final String beanName = transformedBeanName(name);
   Object bean;
   // 重点方法
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      try {
         if (mbd.isSingleton()) {
            // 重点方法
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
      }
   }
   return (T) bean;
}

来看看第一个getSingleton方法的内部实现

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 从一级缓存中获取该bean
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			// 从二级缓存中获取该bean
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				// 从三级缓存中获取该bean对应的singletonFactory
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					// 返回该bean对象(三级缓存中)
					singletonObject = singletonFactory.getObject();
					// 将该bean放到二级缓存
					this.earlySingletonObjects.put(beanName, singletonObject);
					// 从三级缓存中删除
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

从而可知,每次调用doGetBean方法的时候,都需要先从缓存中获取对象,如果缓存中没有的话,再去创建。

再看看第二个getSingleton方法,先来看看它是如何调用的?

getSingleton(beanName, () -> {
	try {
		return createBean(beanName, mbd, args);
	}
	catch (BeansException ex) {
		// ...
	}
});

如果你不懂lambda表达式,其实上述的代码就等同于—>

getSingleton(beanName, new ObjectFactory() {
	@Override
	public T getObject() {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// ...
		}
	}
});

再来看看这个getSingleton方法的具体实现

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   synchronized (this.singletonObjects) {
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
      	 // 将当前bean添加到singletonsCurrentlyInCreation中,表示该bean正在经历创建过程中,后面会用到判断(比如上述的getSingleton中if的判断)
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         try {
         	// 其实就是调用createBean方法
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (BeanCreationException ex) {
           	// ...
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
         	// 将创建完的bean添加到一级缓存,并且从二、三级缓存中删除(执行到这里说明该bean已经执行完其生命周期了)
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

其中的addSingleton,将创建完的bean添加到一级缓存,并且从二、三级缓存中删除(执行到这里说明该bean已经执行完其生命周期了)

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		// 将该bean添加到一级缓存
		this.singletonObjects.put(beanName, singletonObject);
		// 从三级缓存中删除
		this.singletonFactories.remove(beanName);
		// 从二级缓存中删除
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

  
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // 实例化bean
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }

   // ...

   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      // 注意这里,在实例化bean之后,会将该bean存入三级缓存中
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   Object exposedObject = bean;
   try {
      populateBean(beanName, mbd, instanceWrapper);
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }
   
   // ...

   return exposedObject;
}

注意,在上面执行完createBeanInstance方法后(通过反射去创建该bean的实例),接着就去执行了addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))方法,也就是将存入三级缓存的操作。

如果你不懂lambda,这段代码等同于:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// ==================》等同于
addSingletonFactory(beanName, new ObjectFactroy() {
    @Override
    public T getObject() {
        return getEarlyBeanReference(beanName, mbd, bean);
    } 
});

开头介绍就提过,三级缓存不直接存放对象,而是存放一个lambda表达式,但是通过这个getObject方法,可以获取到对象。具体为什么这么实现,这关系到循环依赖是否涉及到动态代理,下面会解释。

下面具体的来看一下doCreateBean中的populateBean方法,也就是给对象注入属性的方法

populateBean ---> applyPropertyValues ---> resolveValueIfNecessary --> resolveReference
注意这个方法,在给当前对象注入对象属性的时候,我们发现,在经过了一列的方法调用后,最后会去执行getBean方法,尝试着从容器中获取这个对象。
在这里插入图片描述

  • 比如说,在上述的例子中,先去实例化A并且给A注入属性,那么就会执行到这里,尝试着通过getBean方法去容器中获取一个B对象;
  • 这个时候,再回到doGetBean方法,上述提到了doGetBean方法在开始就会先去缓存中获取对象,而这个时候缓存中是不存在对象B的,所以下一步就是实例化B对象,然后给B对象注入属性(这里就和A的过程一样了);
  • 给B注入属性的过程中,必然也会执行到getBean里,尝试去容器中获取A对象,那么又执行到doGetBean,开头先去缓存中查找该对象,因为前面实例化A对象之后,就会存入三级缓存(上面说了),所以这里从三级缓存中就可以拿到一个A对象,这样就完了对象B的注入。

大概的执行过程就是这样,具体的请看流程图。

为什么要有第三级缓存?

在上述的例子中,我们并没有提到使用AOP下循环依赖的情况。

那么,现在考虑一下,假设A对象有一个对应的代理对象,也就是实现了AOP。我们在创建B对象时,给其注入A属性,这个注入的属性对象必须是A的代理对象吧!

首先,你需要知道,一般情况下,某个类的代理对象是在什么时候生成的?

在其进行初始化操作,执行后置处理器的时候,如果该类实现了AOP,则在这一步才会生成代理对象。
在这里插入图片描述

那么,基于这种情况,看看下面的例子。

假设没有第三级缓存

执行步骤:

  • 首先,实例化A对象,将其直接放入二级缓存中(原本这里是放入三级缓存的);
  • 注入A对象的属性,发现需要一个B对象;
  • 实例化B对象,将其直接放入二级缓存中(原本这里是放入三级缓存的);
  • 注入B对象中的A属性;

这个时候,发现问题了。二级缓存中保存的是一个A的半成品对象,但是A的代理对象还没有生成(A对象才执行到半路就去创建B对象了),我们在对B对象中的A属性进行注入的时候,只能从二级缓存中获取到这个普通的A对象,而并非A的代理对象。

这个时候三级缓存诞生了。

我们都有注意到,三级缓存中存放的是一个ObjectFactory对象,其实就是一个lambda表达式。
在这里插入图片描述
在这里插入图片描述

getEarlyBeanReference

重点来说一下这个方法。

前面说到,一般情况下,bean的代理对象都是在执行后置处理器的时候才会生成。那么如果遇到上面这种情况,那么B对象在注入A属性的时候根本无法获取到A的代理类(A本身就没有执行到后置处理器这一步,还没生成代理对象呢)。

因此,Spring提供了一个getEarlyBeanReference方法,如果对象实现了AOP,则调用该方法直接就能返回其代理对象。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            // 创建并返回该bean的代理对象
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
         }
      }
   }
   return exposedObject;
}

在这里插入图片描述

并且这个方法作为lambda表达式保存到了三级缓存中。

这个时候,步骤就变成了这样:

  • 首先,实例化A对象,将其直接放入三级缓存中(value: lambda表达式);

  • 注入A对象的属性,发现需要一个B对象;

  • 实例化B对象,将其直接放入三级缓存中;

  • 注入B对象中的A属性,直接从三级缓存中获取到这个lambda表达式,然后调用getEarlyBeanReference,提前就把A的代理对象拿出来了,并且将这个代理对象放入二级缓存中,从三级缓存中删除

  • 进行注入操作,将获取到的A的代理对象注入到B对象的属性中;

  • 此时B对象已经是一个完整的对象了;

  • 这个时候再回来,继续初始化A对象;

  • A对象拿到刚刚返回的B对象(完整的B对象),对其属性B进行注入

    注意,这里的A对象还是之前那个普通的A对象,并不是其代理对象。

  • A对象完成属性的注入,再去执行后置处理器生成代理对象时,就不需要带去做别的操作了,直接从二级缓存中拿刚刚的那个代理对象即可;

  • 初始化完毕,对象保存到一级缓存,情况二三级缓存。

流程图:

在这里插入图片描述

仅仅使用二级缓存行不行?

如果循环依赖的对象不存在生成AOP,也就是不去调用getEarlyBeanReference方法时,完全可以。

这个时候,又听到了一个问题,直接将ObjectFactory保存到二级缓存中不就行了吗?

答案:也是可以的。

但是

如果是这样,那么在进行创建A对象的操作过程中,就需要提前执行它的后置处理器获取其代理对象(也就是在创建完实之后,就去获取它的代理对象),然后再去注入其属性,创建B对象的流程。

我的理解是,这样做完全打乱了Bean的生命周期了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值