问题产生原因
循环依赖是什么
循环依赖是指 spring 中,两个类各自注入对方实例到自身字段的一种情况。由于双方互相注入,因此形成了循环依赖。比如 beanA 中注入了 B 实例,B 中注入了 A 实例。如下代码所示:
// A依赖了B
class BeanA{
@Autowired
public BeanB b;
}
// B依赖了A
class BeanB{
@Autowired
public BeanA a;
}
为什么这样会产生问题?
正常的互相依赖是不会产生问题的,如下所示
BeanA a = new BeanA();
BeanB b = new BeanB();
a.b = b;
b.a = a;
而 spring 的创建流程可以简化为以下三步,实例化、填充Bean,初始化。
在填充 bean 时,会执行 Autowired 注入,如果在容器中没找到对应的对象,则会走创建流程,因此导致循环依赖问题,如下所示:
spring 的解决方案
三级缓存
在 Spring 中是通过三级缓存去处理的。三级缓存如下所示:
singletonObjects:一级缓存,存放成品的 bean。可以对外使用
earlySingletonObjects:二级缓存,在被提前引用后,存放被代理后的对象(如果有被代理的话),不能对外使用
singletonFactories:三级缓存,存放对象工厂,用于获取被提前引用的 bean,主要解决循环依赖问题,不能对外使用
在加入了缓存后,处理流程大致如下:
上图没有太明显的执行次序,这里简单补充一张创建过程,可以对照着看一下
由上图可以得知,在加入三级缓存后,创建 B 时,填充 bean 流程不会再次执行 spring 的实例化A流程,而是从缓存中获取对象工厂,由此获取代理对象并注入,解决了循环依赖的问题。
关键源码解析
- 创建 bean 流程
- 源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 创建 bean 流程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 实例化 bean
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
// 省略。。。
// 放入三级缓存
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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 包装成 ObjectFactory 对象放入三级缓存
// getEarlyBeanReference 方法为:调用 bean 后处理器,提前获取代理对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充 bean,调用 bean后处理器,执行 autowired 等注解的解析注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean
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);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
// 如果被提前引用,则获取被提前引用的对象(代理增强),然后将该对象返回
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 省略。。。
}
// 省略。。。
return exposedObject;
}
- 依赖注入时,从缓存中获取 bean
- 源码位置:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// 这里 getSingleton,从三级缓存中获取 beanA
// 然后调用三级缓存中的对象工厂,提前创建代理对象,并放入二级缓存中
// Eagerly check singleton cache for manually registered singletons.
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 + "'");
}
}
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 省略。。。
try {
// 省略。。。
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 如果是单例 bean,缓存中没有该对象,则走创建流程
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 省略。。。
}
// 省略。。。
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
- 从缓存中获取 bean
- 源码位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 逐级查找,先从一级缓存中查
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 再从二级缓存中查找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 二次检查
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 从三级缓存中获取对象工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果有的话,则从对象工厂中获取被提前引用的 bean
singletonObject = singletonFactory.getObject();
// 然后放入二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
- 通过对象工厂获取被提前引用的 bean
- 源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 通过后处理器,获得代理对象(如果有的话)
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
b. 补充,主要使用该方法进行代理增强:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理则进行代理增强
return wrapIfNecessary(bean, beanName, cacheKey);
}
注意事项
- 如果没有循环依赖,则正常的代理增强不会通过对象工厂创建,而是通过 bean 后处理器 BeanPostProcessor.postProcessAfterInitialization 方法创建,具体实现参考源码:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
- spring 通过三级缓存区分成型的 bean、已经被提前引用(代理)后的 bean、未成型且未被提前引用的 bean。因此如果已经被提前引用(代理增强)了,则不会被重复代理
为什么这么解决
** 以下内容为个人理解,仅供参考,如有错误之处,还望指出,互相交流 _ **
仅一级缓存的问题
个人认为,实际上只要有一级缓存也能解决循环依赖的问题。如下图所示:
将 bean 实例化好后,在填充前就放入到缓存中,这样就算需要依赖注入,也可以直接从缓存中获取,但这种需要一级缓存中允许存放未成品的 bean。
如果一级缓存允许存放未成品的 bean,那么一些懒加载的 bean,可能会在容器提供服务后被并发调用创建,在这种情况下,可能会将未成品的 bean 提供出去使用,由此会带来一些问题。如下图所示
只有一级缓存的情况:
仅二级缓存的解决方案
而二级缓存则是防止这种情况,将未成品的 bean 放入到二级缓存中,成品的 bean 放入到一级缓存,这样就不会产生这个问题,也能解决循环依赖。
有二级缓存的情况:
代理问题的解决
如果仅有二级缓存,那么出现循环依赖中需要被代理时,则需要解决两个问题
- 代理类何时创建?
- 放入二级缓存的是代理类还是原始类?
代理类的创建时机可以有两个:
-
实例化后,直接产生代理对象并放入二级缓存;
-
在被提前引用时,创建代理类。
第二种方案在仅有二级缓存中行不通,因为如果是被提前引用时再创建,那么可能会有以下问题: -
被提前引用后,如果将代理类放入二级缓存中做替换,那么其他类需要提前引用时,如何区分是否已经被代理,不需要再被代理?
-
如果将代理类放入一级缓存,则可能会出现前面说的容器初始化完成后的并发调用问题
-
如果是每次被提前引用时,都创建新的代理对象,则对象引用错误
方案一则没问题,后续流程可以正常执行,但是 spring 并没有采用这种方案,而是使用了三级缓存去解决
三级缓存的加入
在三级缓存的设计中,第三级缓存存放 ObjectFactory,二级缓存存放被提前创建的代理 bean,一级缓存存放成品 bean。
这个设计应该是 spring 为了不影响其他正常的 bean 的创建流程所做的特殊处理。所以在设计时,并没有采用二级缓存,提前创建代理类的方式,去打乱创建流程。而是采用三级缓存的方式,对这些特殊的 bean 做的特殊处理(如果是正常的创建流程,则会在填充和初始化 bean 后,通过 bean 后处理器去创建代理对象)。
当出现循环依赖时,则通过三级缓存中的 ObjectFactory 去获取被代理的 bean,提供依赖注入使用,然后将其存放到二级缓存中,表示该类已经执行过代理增强方法。如果和其他类还存在循环依赖,则直接引用二级缓存中已经被代理过的 bean 即可。
以上。。。