0、循环依赖说明

循环依赖很常见。

比如在Service层,AService注入了BService,BService又注入了AService,就会导致循环依赖。

SpringBoot 在2.6.0之前的版本,自动处理了循环依赖问题。

2.6.0之后版本出现了循环依赖会项目启动失败,需要手动处理。

1、循环依赖解决方案

循环依赖的校验是为了保证架构代码的解耦,出现循环依赖意味着业务代码还有很大解耦空间。如果想要快速解决循环依赖,而不想重构业务代码,可以采用下面方式:

1)yml配置运行循环依赖

spring:
  main:
    allow-circular-references: true

2)在循环依赖发生的Bean上都添加@Lazy注解

@Autowired
@Lazy
private SysUserService sysUserService;

2、Spring解决循环依赖的底层原理

采用三级缓存机制。下面介绍三级缓存分别执行的操作:

一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例。

二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean实例。

三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会。

具体发生循环依赖时执行的业务逻辑如下:

1)创建A实例,放入三级缓存,此时对象不完整,只是进行了地址曝光,让人知道有这么个实例诞生了。

2)A进入三级缓存之后,就想着进入二级缓存。然后A实例开始注入属性,例如属性B,然后发现属性B还没有被创建,于是开始取创建B,在创建的过程发现B引用了A(A一阵无语,循环依赖到自己头上了)。

3)B在实例化的过程,也会先进入三级缓存,同时想要进入二级缓存,于是会先去三级缓存找A,三级缓存找到了A就会顺手把A放入到二级缓存,A在B的帮助下进入二级缓存,同时也在不断完善这A自身的一些属性,属性完善之后就会进入到一级缓存。B因为知道二级缓存的A是不完整的,就会去一级缓存中寻找完成的A,一旦在一级缓存中找打完成的A,就会将二级缓存的A删除。

参考文章:https://download.csdn.net/blog/column/11976288/126450801

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐