Spring循环依赖

Spring的bean的生命周期

其定义为:从对象的创建到销毁的过程。而Spring中的一个Bean从开始到结束经历很多过程,但总体可以分为六个阶段Bean定义、实例化、属性赋值、初始化、生存期、销毁

为什么会出现循环依赖?

首先,什么是循环依赖?

spring容器的bean互相依赖形成闭环,称为spring的循环依赖,要求是单例bean(默认,@scope(“prototype”)),多例(原型bean,@Scope(“prototype”))是不能实现循环依赖的。

@service
class AServiceImpl{
    @Autowried
    private BServiceImpl BServiceImpl;
}
@service
class BServiceImpl{
    @Autowried
    private AServiceImpl aServiceImpl;
}

三级缓存

缓存字段名缓存级别数据类型解释
singletonObjects1Map<String,Object>保存的是完整的Bean,即可以使用的Bean
earlySingletonObjects2Map<String,Object>保存的是半成品的Bean,即属性还没有设置,没有完成初始化工作
singletonFactories3Map<String,ObjectFactory<?>>主要是生成Bean,然后放到二级缓存中

一级缓存singletonObjects的作用是什么?

第1级缓存 用于存放 已经属性赋值、完成初始化的 单列BEAN

//第1级缓存 用于存放 已经属性赋值、完成初始化的 单列BEAN
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存earlySingletonObjects的作用是什么?

第2级缓存 用于存放已经实例化,还未做代理属性赋值操作的 单例BEAN

//第2级缓存 用于存放已经实例化,还未做代理属性赋值操作的 单例BEAN
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存singletonFactories的左右是什么?

第3级缓存 存储创建单例BEAN的工厂

//第3级缓存 存储创建单例BEAN的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

为什么使用三级缓存

解决代理对象(如aop)循环依赖的问题。

例: a依赖b,b依赖a,同时a,b都被aop增强。

首先明确aop的实现是通过 postBeanProcess后置处理器,在初始化之后做代理操作的。

为什么使用三级缓存原因:

1 只使用二级缓存,且二级缓存缓存的是一个不完整的bean

如果只使用二级缓存,且二级缓存缓存的是一个不完整的bean,这个时候a在设置属性的过程中去获取b(这个时候a还没有被aop的后置处理器增强),创建b的过程中,b依赖a,b去缓存中拿a拿到的是没有经过代理的a。就有问题。

2 使用二级缓存,且二级缓存是一个工厂方法的缓存

如果二级缓存是一个工厂的缓存,在从缓存中获取的时候获取到经过aop增强的对象。可以看到从工厂缓存中获取的逻辑。

protected ObjectgetEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

Object exposedObject = bean;

  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

for (BeanPostProcessor bp : getBeanPostProcessors()) {

if (bpinstanceof SmartInstantiationAwareBeanPostProcessor) {

SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;

            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

        }

}

}

return exposedObject;

}

a依赖b,b依赖a,c。c又依赖a。a,b,c均aop增强。

加载开始: a实例化,放入工厂缓存,设置b,b实例化,设置属性,拿到a,此时从工厂缓存中拿到代理后的a。由于a没加载完毕,不会放入一级缓存。这个时候b开始设置c,c实例化,设置属性a,又去工厂缓存中拿对象a。这个时候拿到的a和b从工厂缓存不是一个对象。出现问题。

3 使用二级缓存,二级缓存缓存的是增强后的bean。这个与spring加载流程不符合。spring加载流程是:实例化,设置属性,初始化,增强。在有循环引用的时候,之前的bean并不会增强后放入到二级缓存。

综上1,2,3 可知二级缓存解决不了有aop的循环依赖。spring采用了三级缓存。

如何去解决循环依赖?

一句话描述,为了解决这个问题,Spring使用了一个名为"临时Bean引用"的技术。它的工作原理是,当一个Bean被创建时,Spring将其放在一个早期暴露的Bean工厂中,然后创建另一个Bean。当创建另一个Bean时,Spring会将第一个Bean的实例注入到第二个Bean的setter方法中。然后,当第一个Bean被完全创建时,Spring将把它的实例注入到第二个Bean的setter方法中。也就是spring三级缓存。

spring内部有三级缓存:

成品:一级缓存 singletonObjects ,用于保存实例化、注入、初始化完成的bean实例 。

半成品:二级缓存 earlySingletonObjects ,用于保存实例化完成的bean实例。

原材料工厂: 三级缓存 singletonFactories ,用于保存bean的创建工厂,以便于后面扩展有机会

创建代理对象。

三级缓存是singletonFactories ,但是是不完整的Bean的工厂Factory,是当一个Bean在new之后(没有属性填充、初始化),就put进去。所以,是原材料工厂。二级缓存是对三级缓存的过渡性处理,只要通过 getObject() 方法从三级缓存的BeanFactory中取出Bean一次,原材料就变成变半成品,就put到二级缓存 , 所以,二级缓存里边的bean,都是半成品一级缓存里面是完整的Bean,是当一个Bean完全创建后(完成属性填充、彻底的完成初始化)才put进去, 所以,是成品。

为什么@Lazy能解决循环依赖?

在Spring Boot中,可以通过使用@Lazy注解来解决循环依赖的问题。

当使用@Lazy注解时,Spring Boot会将Bean对象的初始化推迟到第一次使用时才进行。这样,当两个或多个Bean对象存在循环依赖时,其中一个Bean对象在初始化时可以直接引用另一个Bean对象的代理对象(未初始化的对象),而不需要等待另一个Bean对象完全初始化后再进行引用。

具体来说,在循环依赖过程中,当BeanA需要引用BeanB时,如果BeanB还未初始化完成,Spring会创建一个代理对象,代替BeanB提供服务,并将代理对象暴露给BeanA。当BeanB初始化完成后,代理对象会被替换成真正的BeanB对象。

需要注意的是,@Lazy注解只能解决单例模式下的循环依赖问题。如果存在原型模式下的循环依赖,则需要使用其他的解决方案。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值