Spring中循环依赖的3种情况

  1. 构造器循环依赖
    构造器的循环依赖是不可以解决的,spring容器将每一个正在创建的bean标识符放在一个当前创建bean池中,在创建的过程一直在里面,如果在创建的过程中发现已经存在这个池里面了,这时就会抛出异常表示循环依赖了。

  2. setter循环依赖
    对于setter的循环依赖是通过spring容器提前暴露刚完成构造,但并未完成其他步骤(如setter注入)的bean来完成的,而且只能决定单例作用域的bean循环依赖,通过提前暴露一个单例工厂方法,从而使其他的bean能引用到该bean.当你依赖到了该Bean而单例缓存里面有没有该Bean的时候就会调用该工厂方法生产Bean。
    为什么不把Bean暴露出去,而是暴露个Factory呢?因为有些Bean是需要被代理的

  3. prototype范围的依赖
    对于“prototype”作用域bean,Spring容器无法完成依赖注入,因为“prototype”作用域的bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。

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

以两个类A和B为例,通过set方式的循环依赖,看看Spring是如何解决的。

@Component
public class A {private B b;public void setB(B b) {this.b = b;}
}
@Component
public class B {private A a;public void setA(A a) {this.a = a;}
}

Spring实例化bean是通过ApplicationContext.getBean()方法来进行的。

  1. Spring尝试通过ApplicationContext.getBean()方法获取A对象的实例。由于Spring容器中还没有A对象实例,因而其会创建一个A对象,并将A并且保存单例缓存中,或者singletonFactories中。
  2. 因为A依赖了B对象,所以尝试递归的通过ApplicationContext.getBean()方法获取B对象的实例
  3. 但是Spring容器中此时也没有B对象的实例,所以会先创建一个B对象的实例。发现依赖A对象时,从单例缓存中查找,如果没有找到会从singletonFactories中查找,从而完成属性注入。
  4. 此时A对象和B对象都已经创建了,并且保存在Spring容器中了,只不过A对象的属性b和B对象的属性a都还没有设置进去。A将B注入成功,并注入A的其他属性值,自此即完成了循环依赖的注入。

源码

  1. doGetBean()是一个关键的方法(中间有省略),是ApplicationContext.getBean()的核心
protected  T doGetBean(final String name, @Nullable final Class requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {// 尝试通过bean名称获取目标bean对象,比如这里的A对象Object sharedInstance = getSingleton(beanName);// 我们这里的目标对象都是单例的if (mbd.isSingleton()) {// 这里就尝试创建目标对象,第二个参数传的就是一个ObjectFactory类型的对象。// 这里是使用Java8的lamada 表达式书写的,// 只要上面的getSingleton()方法返回值为空,则会调用这里的getSingleton()方法来创建 目标对象sharedInstance = getSingleton(beanName, () -> {try {// 尝试创建目标对象return createBean(beanName, mbd, args);} catch (BeansException ex) {throw ex;}});}return (T) bean;
}

第一个getSingleton()方法的作用是尝试从单例池中获取Bean,如果没有获取到,则判断是否正在创建中,且有工厂方法。有工厂方法,使用工厂方法创建Bean实例。

第二个getSingleton()方法的作用是尝试创建目标对象,并且为该对象注入其所依赖的属性。

  1. 第一个getSingleton()方法:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 尝试从单例池中获取目标对象,如果存在,则直接返回Object singletonObject = this.singletonObjects.get(beanName);// 如果单例池不存在目标对象,则判断当前对象是否已经处于创建中。if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// singletonFactories是一个Map,其key是bean的名称,值是一个ObjectFactory对象。// 取得Bean的工厂方法,如果存在通过工厂方法类创建Bean(singletonFactory.getObject())ObjectFactory singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 创建目标对象的实例singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
  1. 第二个getSingleton()方法:
//创建单例模式Bean的实例对象if (mbd.isSingleton()) {//这里使用了一个工厂方法类(匿名内部类),定义了ObjectFactory的getObject的实现。sharedInstance = getSingleton(beanName, () -> {try {//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义return createBean(beanName, mbd, args);}catch (BeansException ex) {//显式地从容器单例模式Bean缓存中清除实例对象destroySingleton(beanName);throw ex;}});//获取Bean的实例对象bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}

生成ObjectFactory的实例,并实现getObject方法。方法中调用createBean()。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 将Bean设置为创建中beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {// 工厂方法创建Bean实例。singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}// 将Bean加入到单例池if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}
}
  1. createBean方法的最终调用了doCreateBean()方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// 实例化当前尝试获取的bean对象。BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}// 判断Spring是否配置了支持提前暴露目标bean,也就是是否支持提前暴露半成品的beanboolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {// 将工厂方法放入到Map中。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}try {// 在初始化实例之后,填充属性及依赖对象。会递归的调用getBean()方法尝试获取目标beanpopulateBean(beanName, mbd, instanceWrapper);} catch (Throwable ex) {// ...}return exposedObject;
}

Spring - DI循环依赖相关推荐

  1. Spring解决循环依赖过程

    依赖注入三种方式 构造器注入 set方法注入 注解方式注入 循环依赖场景 我们知道spring是通过依赖注入(DI)来完成属性的填充,而循环依赖是指实例化两个bean互相依赖对方的情况,比如A依赖B, ...

  2. 图解Spring解决循环依赖

    点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 来源:juejin.cn/post/684490412216 ...

  3. spring 循环依赖_简单说说 Spring 的循环依赖

    作者 | 田伟然 回首向来萧瑟处,归去,也无风雨也无晴. 杏仁工程师,关注编码和诗词. 前言 本文最耗时间的点就在于想一个好的标题, 既要灿烂夺目,又要光华内敛,事实证明这比砍需求还要难! 由于对象之 ...

  4. Spring当中循环依赖很少有人讲,今天让我们来看看吧

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  5. Spring当中循环依赖很少有人讲,今天一起来学习!

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  6. 被问麻了,Spring 如何处理循环依赖?

    点击关注公众号,利用碎片时间学习 前言 Spring如何处理循环依赖?这是最近较为频繁被问到的一个面试题,在前面Bean实例化流程中,对属性注入一文多多少少对循环依赖有过介绍,这篇文章详细讲一下Spr ...

  7. Spring IOC循环依赖问题

    Spring IOC循环依赖问题 什么是循环依赖 循环依赖其实就是循环引用, 也就是两个或者两个以上的Bean互相持有对方,形成闭环,例如:A依赖B,B依赖C,C又依赖于A. Spring中循环依赖的 ...

  8. Spring IOC循环依赖

    Spring IOC循环依赖 什么是循环依赖 Spring中Bean实例的创建流程 Spring三级缓存 ClassA创建流程 思考 什么是循环依赖 ClassA中依赖ClassB ClassB中依赖 ...

  9. Spring中循环依赖问题

    1.什么是Spring中的循环依赖 循环依赖就是循环引用,也就是两个或者两个以上的Bean相互持有对方,最终形成闭环.比如A依赖于B,B依赖于C,C又依赖于A. 2.Spring处理循环依赖的机制 无 ...

  10. 【源码分析】Spring的循环依赖(setter注入、构造器注入、多例、AOP)

    写在前面 首先最简单的循环依赖demo就是:A->B 且 B->A.本文围绕这个例子去讲解setter注入的循环依赖.构造器注入循环依赖.多例的循环依赖.带AOP的循环依赖.以下是一些结论 ...

最新文章

  1. C语身教程第三章: C说话挨次筹算匹面(1)
  2. java bytebuffer 大小_java – 为什么DirectByteBuffer.array()有额外的大小?
  3. Python匿名函数——lambda表达式
  4. Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
  5. zhajinhua2012邮件等网络服务的广泛应用
  6. crontab 每天凌晨12点定时器_每天摆摊到凌晨5点,94岁煎饼奶奶火了,一句话惊醒无数中学生!...
  7. win10新建管理员账户_电脑技巧|Win10家庭版如何开启超级管理员账户
  8. 用cglib生成的代理类取不到注解的问题
  9. 昨天要成为反弹一日游?关键看下午了
  10. java算法——哈希表 电话号码查询系统
  11. 四阶龙格库塔法解一维扩散方程
  12. ASAN Runtime【源码分析】(一)——初始化
  13. 学生信息管理系统——错误集锦(五)
  14. 6.3.3 非信贷交易信息 6.3.4 公共信息 6.3.5 查询记录概要
  15. 从TCP拥塞本质看BBR算法及其收敛性(附CUBIC的改进/NCL机制)
  16. 技术控必读 从Type-A到Type-C发展历程
  17. 百度云生态分享日| 网络技术及应用主题沙龙圆满落幕
  18. H5 语音合成播报功能
  19. Received disconnect from xxx.xxx.xxx port 22:2: Too many authentication failures for git
  20. 记录秋招服务器开发面经(游戏服务器)

热门文章

  1. ECMAScript 基础--原始值和引用值
  2. android布局配置
  3. 武汉Apache Hadoop大数据平台,金融行业如何搭建大数据平台?数据采集、分析、处理如何实现?...
  4. tar.xz如何解压:linux和windows下tar.xz解压命令介绍
  5. Windows 10的成功能让苹果学到点什么?
  6. HDU-单词的前缀 字典树
  7. 谈谈主策划需要的能力
  8. Spring框架利用PropertyPlaceholderConfigurer初始化加载多properties文件
  9. 16.高性能MySQL --- MySQL用户工具
  10. 12.高性能MySQL --- 高可用性