作者:服务端开发

blog.csdn.net/u010013573/article/details/90573901

一、循环依赖

  • spring的循环依赖主要是指两个类相互之间通过@Autowired自动依赖注入对方,即类A包含一个类B的对象引用并需要自动注入,类B包含一个类A的对象引用也需要自动注入。

  • 对于循环依赖问题,spring根据注入方式的不同,采取不同的处理策略,对于双方都是使用属性值注入或者setter方法注入,则spring可以自动解决循环依赖注入问题,应用程序可以成功启动;对于双方都是使用构造函数注入对方或者主bean对象(Spring在启动过程中,先加载的bean对象)使用构造函数注入,则spring无法解决循环依赖注入,程序报错无法启动。

二、可以解决的循环依赖

能够解决循环依赖的情况

  • 主bean通过属性或者setter方法注入所依赖的bean,不是通过构造函数注入。

循环依赖解决的实现

实现基础

循环依赖的解决:三级缓存实现,即singletonObjects,earlySingletonObjects和singletonFactories。如下为DefaultSingletonBeanRegistry的getSingleton方法实现:该方法主要在AbstractBeanFactory的doGetBean方法调用。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 检查一级缓存singletonObject是否存在Object singletonObject = this.singletonObjects.get(beanName);// 当前还不存在这个单例对象,// 且该对象正在创建中,即在singletonsCurrentlyInCreation列表中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 检查二级缓存earlySingletonObjects是否存在singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 检查三级缓存singletonFactory是否可以创建ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 三级缓存的对象工厂创建该对象singletonObject = singletonFactory.getObject();// 放入二级缓存earlySingletonObjects中this.earlySingletonObjects.put(beanName, singletonObject);// 移除一级缓存this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
实现过程

1.在创建主bean对象时,AbstractBeanFactory调用DefaultSingletonBeanRegistry的getSingleton方法:将主bean放入singletonsCurrentlyInCreation列表中,从而使得以上的三级缓存实现方法getSingleton,能够进入二三级缓存earlySingletonObjects,singletonFactories查找这个主bean,主要是给依赖bean查找主bean时使用的。

2.接着会调用AbstractAutowireCapableBeanFactory的createBean方法,而createBean方法会调用AbstractAutowireCapableBeanFactory的doCreateBean方法,doCreateBean的方法实现如下:先调用addSingletonFactory方法,在三级缓存singletonFactories中放入主bean的类型为ObjectFactory的singletonFactory对象创建工厂,这样在创建所依赖的bean对象时,可以通过三级缓存机制获取到主bean对象引用。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// 省略其他代码// 1. 调用构造函数创建该bean对象,若不存在构造函数注入,顺利通过instanceWrapper = createBeanInstance(beanName, mbd, args);// 2. 在singletonFactories缓存中,放入该bean对象,以便解决循环依赖问题addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));// 3. populateBean方法:bean对象的属性赋值populateBean(beanName, mbd, instanceWrapper);// 省略其他代码
}

3.在doCreateBean方法中,调用populateBean进行属性赋值,其中步骤2的addSingletonFactory方法是在调用populateBean方法之前调用的,故在调用populateBean对主bean对象进行属性值注入或者setter注入时,主bean的创建工厂已经在singletonFactories缓存中了。

4.在populateBean方法中查找或者创建所依赖的bean对象。在创建所依赖的bean对象时,会调用到AbstractBeanFactory的doGetBean方法。而如果存在循环依赖,则所依赖的bean可以通过属性注入,setter方法注入,或者通过构造函数注入主bean:

  1. 如果通过属性值或者setter方法注入,则没有循环依赖问题。因为主bean和所依赖的bean都可以成功调用构造函数创建对象,通过对象引用来引用对方。属性值注入和setter方法注入,通过bean对象的后置处理器BeanPostProcessor来对该bean对象的属性值进行注入;

  2. 如果该依赖bean对象通过构造函数注入主bean对象,则在调用构造函数创建该依赖bean对象,需要查找主bean对象时,查找时会继续调用到AbstractBeanFactory的doGetBean方法获取主bean对象,而在AbstractBeanFactory的doGetBean方法调用getSingleton方法检查三级缓存是否可以得到主bean对象。

  3. 由步骤1和步骤2可知,由于getSingleton方法的singletonsCurrentlyInCreation和singletonFactories都已经有了主bean的相关信息,通过三级缓存可以查找到主bean对象,即通过主bean对象的创建工厂singletonFactories创建提前曝光的主bean对象,并放入二级缓存earlySingletonObjects,故可以解决为这个依赖bean对主bean的依赖注入,从而成功创建该依赖对象bean。

5.当创建好这个依赖bean对象之后,主bean的属性可以完整注入成功。而由于该所依赖bean包含了主bean中对象引用,故该所依赖的bean对象可以访问和使用主bean,该所依赖的bean对象也是完整的对象,从而最终解决了主bean和所依赖bean之间的循环依赖问题。

6.最后,注入成功之后,都会将单例bean对象放入一级缓存singletonObjects中,并移除存放提前曝光对象的二级缓存earlySingletonObjects和该bean对象的创建工厂缓存earlySingletonObjects,如下为DefaultSingletonBeanRegistry的getSingleton方法实现和addSingleton方法实现:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {// 省略其他代码// 将当前beanName放入singletonsCurrentlyInCreation中,以便解决循环依赖问题beforeSingletonCreation(beanName);try {// getObject方法会调用AbstractAutowireCapableBeanFactory的createBean方法singletonObject = singletonFactory.getObject();newSingleton = true;}// 省略其他代码if (newSingleton) {// 创建好bean对象后,放入singleObjects缓存中addSingleton(beanName, singletonObject);}return singletonObject;}
}protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {// 放入一级缓存this.singletonObjects.put(beanName, singletonObject);// 移除二三级缓存this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}

三、无法解决的循环依赖问题

  • 在主bean中通过构造函数注入所依赖的bean。

  • 如下controller为主bean,service为所依赖的bean:

@RestController
public class AccountController {private static final Logger LOG = LoggerFactory.getLogger(AccountController.class);private AccountService accountService;// 构造函数依赖注入// 不管是否设置为required为true,都会出现循环依赖问题@Autowire// @Autowired(required = false)public AccountController(AccountService accountService) {this.accountService = accountService;}}@Service
public class AccountService {private static final Logger LOG = LoggerFactory.getLogger(AccountService.class);// 属性值依赖注入@Autowiredprivate AccountController accountController;}

启动打印如下:


***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  accountController defined in file [/Users/xieyizun/study/personal-projects/easy-web/target/classes/com/yzxie/easy/log/web/controller/AccountController.class]
↑     ↓
|  accountService (field private com.yzxie.easy.log.web.controller.AccountController com.yzxie.easy.log.web.service.AccountService.accountController)
└─────┘

如果是在主bean中通过属性值或者setter方法注入所依赖的bean,而在所依赖的bean使用了构造函数注入主bean对象,这种情况则不会出现循环依赖问题。

@RestController
public class AccountController {private static final Logger LOG = LoggerFactory.getLogger(AccountController.class);// 属性值注入@Autowiredprivate AccountService accountService;}@Service
public class AccountService {private AccountController accountController;// 构造函数注入@Autowiredpublic AccountService(AccountController accountController) {this.accountController = accountController;}}

四、总结

  • 当存在循环依赖时,主bean对象不能通过构造函数的方式注入所依赖的bean对象,而所依赖的bean对象则不受限制,即可以通过三种注入方式的任意一种注入主bean对象。

  • 如果主bean对象通过构造函数方式注入所依赖的bean对象,则无论所依赖的bean对象通过何种方式注入主bean,都无法解决循环依赖问题,程序无法启动。

  • 原因主要是主bean对象通过构造函数注入所依赖bean对象时,无法创建该所依赖的bean对象,获取该所依赖bean对象的引用。因为如下代码所示。

  • 创建主bean对象,调用顺序为:

1.调用构造函数,2. 放到三级缓存,3. 属性赋值,其中调用构造函数时会触发所依赖的bean对象的创建。

     // bean对象实例创建的核心实现方法protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// 省略其他代码// 1. 调用构造函数创建该bean对象,若不存在构造函数注入,顺利通过instanceWrapper = createBeanInstance(beanName, mbd, args);// 2. 在singletonFactories缓存中,放入该bean对象,以便解决循环依赖问题addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));// 3. populateBean方法:bean对象的属性赋值populateBean(beanName, mbd, instanceWrapper);// 省略其他代码return exposedObject;}
  • createBeanInstance是调用构造函数创建主bean对象,在里面会注入构造函数中所依赖的bean,而此时并没有执行到addSingletonFactory方法来添加主bean对象的创建工厂到三级缓存singletonFactories中。

  • 故在createBeanInstance内部,注入和创建该主bean对象时,如果在构造函数中存在对其他bean对象的依赖,并且该bean对象也存在对主bean对象的依赖,则会出现循环依赖问题,原理如下:

    主bean对象为A,A对象依赖于B对象,B对象也存在对A对象的依赖,创建A对象时,会触发B对象的创建,则B无法通过三级缓存机制获取主bean对象A的引用(即B如果通过构造函数注入A,则无法创建B对象;如果通过属性注入或者setter方法注入A,则创建B对象后,对B对象进行属性赋值,会卡在populateBean方法也无法返回)。
    故无法创建主bean对象所依赖的B,创建主bean对象A时,createBeanInstance方法无法返回,出现代码死锁,程序报循环依赖错误。

琐碎时间想看一些技术文章,可以去公众号菜单栏翻一翻我分类好的内容,应该对部分童鞋有帮助。同时看的过程中发现问题欢迎留言指出,不胜感谢~。另外,有想多了解哪些方面内容的可以留言(什么时候,哪篇文章下留言都行),附菜单栏截图(PS:很多人不知道公众号菜单栏是什么)

END

我知道你 “在看”

来谈谈Spring构造函数注入的循环依赖问题相关推荐

  1. spring 构造函数注入_Spring依赖注入–字段vs设置器vs构造函数注入

    spring 构造函数注入 欢迎使用Spring Dependency Injection –字段,设置器,构造函数注入教程. 了解场注入 , 二传手注入和构造函数注入之间的区别. 借助代码示例,我们 ...

  2. Spring依赖注入和循环依赖问题分析

    Spring源码揭秘之依赖注入和循环依赖问题分析 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGet ...

  3. Spring源码分析-循环依赖

    导语   前面提到了实例化Bean其实是一个复杂的过程,而在这个过程中比较难以理解的就是循环依赖的问题,下面就先来看看什么是循环依赖 文章目录 什么是循环依赖? Spring 是怎么解决循环依赖的? ...

  4. Spring是如何解决循环依赖的

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的.这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够 ...

  5. 万字长文带你吃透Spring是怎样解决循环依赖的

    在Spring框架中,处理循环依赖一直是一个备受关注的话题.这是因为Spring源代码中为了解决循环依赖问题,进行了大量的处理和优化.同时,循环依赖也是Spring高级面试中的必考问题,回答得好可以成 ...

  6. Spring三级缓存解决循环依赖问题详解

    spring三级缓存解决循环依赖问题详解 前言 这段时间阅读了spring IOC部分的源码.在学习过程中,自己有遇到过很多很问题,在上网查阅资料的时候,发现很难找到一份比较全面的解答.现在自己刚学习 ...

  7. 【Spring源码:循环依赖】一文弄懂Spring循环依赖

    1. 什么是循坏依赖 很简单,其实就是互相依赖对方,比如,有一个A对象依赖了B对象,B对象又依赖了A对象. // A依赖了B public class A{private B b; }// B依赖了A ...

  8. Spring的getBean解决循环依赖

    Spring是如何解决循环依赖的? 通过三级缓存提前暴露对象解决的. 三级缓存存放了哪些对象信息? 一级缓存存放的是完整对象. 二级缓存存放的是那些属性还没赋值的对象. 三级缓存存放的是ObjectF ...

  9. spring 构造函数注入_Spring构造函数依赖注入示例

    spring 构造函数注入 欢迎使用Spring构造函数依赖注入示例指南. 基于构造器的依赖注入是Spring 依赖注入的一种 . 依赖注入的另一种类型是Setter注入和字段注入. 有关Spring ...

最新文章

  1. python 指定字符串长度_Python指定字符串的长度,主要是
  2. R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化、并添加y=0的水平横线)、uniroot函数求解方程的根(并添加方程根对应的垂直竖线)
  3. linux 网络端口状态,Linux下用netstat查看网络状态、端口状态(转)
  4. git pull 报错:git - error: RPC failed; curl 18 transfer closed with outstanding read data remaining 解决
  5. WPF Template模版之DataTemplate与ControlTemplate的关系和应用【二】
  6. python点击网页按钮 没有id_button没有id,没有onclick事件。点击却有提交的功能,如何实现的?...
  7. 有什么推荐的计算机视觉项目?来自微软亚研院的清单
  8. 蔚来:ET7首批预生产车正式下线
  9. 提高你css技能的css开发技巧
  10. html5 手机上传视频,【报Bug】手机h5端收不到选择视频以及上传视频回调
  11. 《WF编程》系列之29 - 本地通信事件:HandleExternalEventActivity 活动生成器
  12. 【有限元分析】提高有限元分析计算精度的h方法和p方法
  13. ThingJS摄像机总结
  14. WinRar DOS命令大全带详细参数rar/zip/7z压缩文件解密
  15. font-spider压缩web font字体
  16. 又一个吊打百度网盘的开源神器,还是99年妹子开发的
  17. DSP F28335时钟及控制系统
  18. html模态框常见问题,模态框无法弹出的问题
  19. 日记20050930
  20. 为matlab GUI添加背景图片

热门文章

  1. 办公室小野与爆米花视频身亡女孩家属和解:补偿金额保密
  2. 李彦宏发布内部信:宣布升级百度“云+AI”战略
  3. 华为P30系列新配色官宣:9月6日IFA2019上见!
  4. 华为P30系列机身侧面照曝光 摄像头仍然“凸起”...
  5. 又搞事!雷军郑重宣告:小米9才是骁龙855全球真首发
  6. 程序员被公司开除,隔阵子领导命令回前公司讲解代码,网友直呼:关我嘛事?
  7. UltraEdit的高亮【原创】
  8. java8获取当前时间并格式化
  9. Ubuntu报“xxx is not in the sudoers file.This incident will be reported” 错误解决方法
  10. 3加密狗计算pin码_6 个芯片打造复古经典计算机:215 色显示,能编程能玩小游戏...