Spring源码之循环依赖

什么是循环依赖?

循环依赖指的是两个或多个对象之间相互依赖的情况。简单来说,当对象A依赖于对象B,而对象B又依赖于对象A时,就会形成循环依赖.
在这里插入图片描述

Spring循环依赖

我们先来看下列服务,结合Bean创建的生命周期可以知道ReimburseService与ExpenseService循环依赖

// 报销单服务
@Component
public class ReimburseService {
	@Autowired
	private ExpenseService expenseService;
	@Autowired
	private InvoiceService invoiceService;
}
// 费用服务
@Component
public class ExpenseService {
	@Autowired
	private ReimburseService reimburseService;
}

在这里插入图片描述

解决

Spring解决循环依赖引入三级缓存
在这里插入图片描述

Spring中的三级缓存

  • singletonObjects
    一级缓存,存放完整生命周期的bean
  • earlySingletonObjects
    二级缓存,存放半成品的bean,没有经历初始化过程的Bean
  • singletonFactories
    三级缓存,用来创建bean对象的工厂

三级缓存的底层实现

让我们直接来到Spring源码

// 直接来到实例化方法
// -> doCreateBean(beanName, mbdToUse, args);
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
	...
	BeanWrapper instanceWrapper = null;
	// 实例化对象-> 这里获取的是普通对象
	instanceWrapper = createBeanInstance(beanName, mbd, args);
	Object bean = instanceWrapper.getWrappedInstance();
	...
	// 如果开启了循环依赖(默认开启)
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			// 放入三级缓存 -> 这里放入的是Bean的构造方法(可能是普通对象或是代理对象)->后面详细讲述这个方法
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
	Object exposedObject = bean;
	try {
		// 属性填充
		populateBean(beanName, mbd, instanceWrapper);
		// 初始化Bean
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}catch{
	...
}
	
}

在属性填充时,去获取属性值,最终会调用方法
Object beanInstance = getSingleton(beanName, false);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 首先去一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     	// 一级缓存没有 -> 二级缓存获取
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
        	// 二级缓存没有 -> 加锁
            synchronized (this.singletonObjects) {
                // 重新一级缓存获取
                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) {
                            // 这里调用的就是getEarlyBeanReference();之前实例化后放入的
                            singletonObject = singletonFactory.getObject();
                            // 将创建的实例对象放入二级缓存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 三级缓存移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

根据我们上述的情况,当ReimburseService会依赖注入ExpenseService,就会去创建ExpenseService,
ExpenseService又会去依赖注入ReimburseService,这时就会调用三级缓存中的ReimburseService去实例化,现在我们具体看下getEarlyBeanReference(…);

// 来到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;
	}
// -> 来到下列方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    // 获取缓存键
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    // 将Bean对象放入早期代理引用缓存中
    this.earlyProxyReferences.put(cacheKey, bean);
    // 对Bean对象进行包装(如果需要)
    return wrapIfNecessary(bean, beanName, cacheKey);
}

上述getEarlyBeanReference()可能返回普通对象,也可能返回代理对象,因为正常生命周期,Bean的AOP是在初始化后执行,然后生成代理对象,如果产生了循环依赖,就必须提前生成代理对象.
结合正常的初始化AOP代码:

/**
 * 在初始化之后对Bean对象进行后置处理。
 * 
 */
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 获取缓存键
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        
        // earlyProxyReferences中对应cacheKey的对象一定是个普通对象X,在getEarlyBeanReference方法中放入的,而且是在wrapIfNecessary()方法之前
        // 如果X == bean说明这个Bean没有实现AOP功能,直接返回普通bean就可以了
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 如果X != bean,说明这个Bean是个代理对象,需要进入wrapIfNecessary()再次判断执行后置处理器的逻辑
            // 里面维护了一个advisedBeans缓存,所以getEarlyBeanReference方法中执行过的AOP不会在执行
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    
    // 返回Bean对象
    return bean;
}

总结

综上:为什么Spring要使用三级缓存来解决循环依赖呢?
正常情况下,其实我们只要两个缓存就可以了,但是由于AOP是在初始化后执行的(AOP产生的是一个代理对象,不是原先的普通Bean对象),所以如果直接拿实例化的普通对象,AOP就会失效.
这里Spring使用了三级缓存,里面存储的是你创建Bean的方法,也就是说可能得到的是个普通对象,也可能是个代理对象.

总得来说,笔者认为打破Spring循环依赖的是二级缓存,也是三级缓存.
在这里插入图片描述
A与B,C都循环依赖了,先依赖注入B,B首次会从三级缓存获取A,然后存到二级缓存,等到依赖注入C的时候,C获取A直接从二级缓存获取.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值