2021-09-14 Spring中的循环依赖问题

Spring中的循环依赖问题

文章目录

什么是循环依赖?

A.class 中调用了 B
B.class 中调用了 A

调用方式,new @Autowired 等等
在这里插入图片描述

如果不考虑spring, 循环依赖不是问题,对象相互依赖很正常

初始概念

了解Bean 的生命周期

0. creatingSet 正在创建的bean,添加到该集合中
1. 实例化–AService原始对象(不完整)(相当于new AService()) --> 存入第三级缓存<‘aService’, AService原始对象> beanName , BeanDefinition
2. 填充Service属性
2.1. 填充AService属性
2.2. 填充aServce–>从单例池找bService–>找不到–>创建bService(spring配 置文件 可以指定创建顺序)
2.3. 实例化BService对象(new BService())
2.4. 填充bServce–>从第一级缓存单例池找 aService -->找不到 -->发现 aService 正在创建中 -->判断aService 出现了循环依赖 -->从第二级缓存找 -->找不到 --> 从第三级缓存拿AService原始对象 -->提前AOP(执行lambda) --> AService代理对象 --> 放入第二级缓存<‘aService’,AService代理对象>

----------------这一步很重要--------
问题1. : 这时候,把一个aSerice 暂时没有值的(不完整的bean对象),填充到了BSerice
到这里,可正常创建,AService对象,导致bServce填充完成,实例化完成,从而可以解开循环引用死循环
问题2. : 多个Service创建,调用同个AService ,会造成多个aService 代理对象,肯定不行啊,这时候需要用到第二级缓存,
问题3. : 第三级缓存,就是

2.5 填充其他属性

如果这时候有个CServic 创建,里面引用了 AService 又要创建AService 提前AOP,出现两个AService代理对象 ,这时候从 第二级缓存<‘aService’,AService代理对象>拿代理对象

2.6 做其他事情
2.7 放入单例池
3. 填充aService其他属性
4. 做其他事–>从第二级缓存取出 AService(原始对象 或 代理对象)这时候已经是完整对象了(上面 执行lambda)

AOP --> AService代理对象

问题 : 这时候,放入单例池的是,AService代理对象,而不是tMap里面的Aservice
想给aService属性(第二步) , 但是代理对象是第四步,所以在第一步就加上AOP,直接在tMap中放入代理对象,

对AService提前进行AOP
什么条件下,需要对对AService提前进行AOP ? 循环依赖的情况下.
为什么不对每一个对象AOP ?
AService代理对象(不完整) 不能放入单例池,都是空的

问题 : 如何判断,是否进行了AOP?
开启AOP要加注解 @EnableAs

5. 放入单例池
6. creatingSet.remove(‘aService’)

如何解决Spring中的循环依赖问题

spring 三级缓存

1. 第一级缓存 : 单例池 —singletonObjects = ConcurrentHashMap<beanName,bean对象>(256)
2. 第二级缓存 : 早期单例对象 earlySingletonObjects = new HashMap<beanName,bean对象>(16)

作用 : 保证,唯一的名字,对应不完整的对象(代理对象)

3. 单例工厂 singletonFactories = new HashMap<beanName,ObjectFactry>(16) [ObjectFactry就是个lambda表达式]

不管用不用,都会存入第三级缓存,给循环依赖,准备工作

@Lazy

其他补充参考信息

ConcurrentHashMap 和 HashMap

ConcurrentHashMap保证线程安全,性能较低
HashMap,线程不安全,性能较高

单例Bean 和 单例模式

(单例Bean,是否是整个spring中只有一个? <否> 和 单例模式<枚举>不一样) ;;;;;单例池 : singletonObjects ConcurrentHasMap <beanName , bean对象>

首先看单例模式,在一个JVM进程中(理论上,一个运行的JAVA程序就必定有自己一个独立的JVM)仅有一个实例,于是无论在程序中的何处获取实例,始终都返回同一个对象,以Java内置的Runtime为例(现在枚举是单例模式的最佳实践),无论何时何处获取,下面的判断始终为真:

//  基于懒汉模式实现
//  在一个JVM实例中始终只有一个实例
Runtime.getRuntime() == Runtime.getRuntime()

与此相比,Spring的单例Bean是与其容器(ApplicationContext)密切相关的,所以在一个JVM进程中,如果有多个Spring容器,即使是单例bean,也一定会创建多个实例,代码示例如下:

//  第一个Spring Bean容器
ApplicationContext context_1 = new FileSystemXmlApplicationContext("classpath:/ApplicationContext.xml");
Person yiifaa_1 = context_1.getBean("yiifaa", Person.class);
//  第二个Spring Bean容器
ApplicationContext context_2 = new FileSystemXmlApplicationContext("classpath:/ApplicationContext.xml");
Person yiifaa_2 = context_2.getBean("yiifaa", Person.class);
//  这里绝对不会相等,因为创建了多个实例
System.out.println(yiifaa_1 == yiifaa_2);

以下是Spring的配置文件:

<!-- 即使声明了为单例,只要有多个容器,也一定会创建多个实例 -->
<bean id="yiifaa" class="com.stixu.anno.Person" scope="singleton">
    <constructor-arg name="username">
        <value>yiifaa</value>
    </constructor-arg>
</bean>

总结
Spring的单例bean与Spring bean管理容器密切相关,每个容器都会创建自己独有的实例,所以与GOF设计模式中的单例模式相差极大,但在实际应用中,如果将对象的生命周期完全交给Spring管理(不在其他地方通过new、反射等方式创建),其实也能达到单例模式的效果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值