精华内容
下载资源
问答
  • spring循环依赖

    2021-01-24 01:39:09
    spring循环依赖 关于spring循环依赖网上有太多的例子,本文只是简单的记录一下。本文默认读者熟悉spring核心之一控制反转和依赖注入 在我们的开发过程中,我们基本上对循环依赖是无感且不用去考虑如何解决。如上图...

    spring循环依赖


    关于spring循环依赖网上有太多的例子,本文只是简单的记录一下。本文默认读者熟悉spring核心之一控制反转和依赖注入

    bean循环依赖案例
    在我们的开发过程中,我们基本上对循环依赖是无感且不用去考虑如何解决。如上图中ClassA使用属性注入了ClassB,ClassB使用属性注入了ClassA。如上图这就是产生了循环依赖,但是如果我们这样启动项目是能启动成功的。这是因为spring已经帮我们解决了部分产生循环依赖的问题。

    首先图解一下spring加载bean流程
    在这里插入图片描述
    解决spring循环依赖很重要的一个思想就是一个中间态

    Spring的三级缓存解决循环依赖的问题

    • singletonObjects:完成初始化的单例bean对象的单例池。这里的bean经历过实例化->属性填充->初始化以及各种后置处理(一级缓存);
    • earlySingletonObjects:存放早期曝光的bean 对象(完成实例化但是尚未填充属性和初始化)。仅仅能作为指针提前曝光,被其他bean所引用,用于解决循环依赖的(二级缓存);
    • singletonFactories:存放早期曝光的bean对象工厂(完成实例化但是尚未填充属性和初始化)。如果允许提前曝光(allowEarlyReference = true),Spring 会将实例化后的 bean 提前曝光,也就是把该 bean 转换 成 beanFactory 并加入到 singletonFactories(三级缓存);

    图解
    在这里插入图片描述
    spring在默认情况下会对创建bean进行自然排序,所以在默认情况下A会有限创建

    依赖情况依赖注入方式是否解决
    类A和类B互相依赖互相采用setter注入
    类A和类B互相依赖互相采用构造器注入
    类A和类B互相依赖类A采用setter注入类B,类B采用构造器注入类A
    类A和类B互相依赖类A采用构造器注入类B,类B采用setter注入类A
    • 所以spring只能解决setter注入的循环依赖是错误的
    • 三级缓存可以提高性能也是错误的(本文没有解析,请自行分析)
    • 最后虚心学习,共同进步。-_-
    展开全文
  • Spring循环依赖

    2020-03-20 16:49:17
    Spring循环依赖 1、 什么是循环依赖 循环依赖就是循环引用,就是两个或多个bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A 例如这张图 三个对象之间相互依赖 2、Spring 怎么...

    插播一条 本来计划学习Spring cloud 面试咨询 问我Spring 依赖注入的时候 有时会有循环依赖的场景 这种Spring内部是如何解决的?
    嗯? 这是什么问题 没准备过啊

    Spring循环依赖

    1、 什么是循环依赖
    循环依赖就是循环引用,就是两个或多个bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A
    在这里插入图片描述
    例如这张图 三个对象之间相互依赖

    2、Spring 怎么解决?
    Spring的循环依赖的理论依据基于Java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的(但是构造器必须是在获取引用之前)

    Spring的单例对象的初始化主要分为三步:

    1. createBeanInstance:实例化,调用对象的构造方法实例化对象
    2. populateBean:填充属性,这一步主要是对bean的依赖属性进行填充
    3. initializeBean:调用Spring xml中的init方法

    从上面单例bean的初始化可以知道:循环依赖主要发生在第一、二步,也就是构造器循环依赖和field循环依赖。那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存

    这三级缓存分别指:

    singletonFactories:单例对象工厂的Cache
    earlySingletonObjects:提前暴光的单例对象的Cache
    singletonObjects:单例对象的Cache
    在创建bean的时候,首先想到的是从Cache中获取这个单例的bean,这个缓存就是singletonObjects。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则从singletonFactories中移除,并放入earlySingletonObjects中(其实也就是从三级缓存移动到了二级缓存)

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory。这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还没有进行初始化的第二步和第三步,但是已经能能够根据对象引用能定位到堆中的对象,所以Spring此时将这个对象提前曝光出来

    “A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况:A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中
    以上完全复制博客https://blog.csdn.net/qq_40378034/article/details/104093014

    说的有些复杂 来简单粗暴一点解读这个事:

    首先理解一个概念 初始化单例对象的三个步骤分别对应着三级缓存, 刚开始在三级缓存中,完全初始化完成后 就在一级缓存中

    1. A和B两个对象 相互依赖 Spring现在要初始化这两个对象
    2. 调用A的构造方法 初始化A实例 放到三级缓存 SingletonFactories中 暴露出来
    3. 然后 初始化A的属性参数 发现 诶 要初始化B对象
    4. 好我们来初始化B实例 调用B的构造方法 初始化B的实例 放到三级缓存中来
    5. 初始化B的属性参数 发现 诶里面 有A实例需要初始化 好 这时幸好已经在三级缓存中 初始化好了A实例 直接放过来 初始化完成
    6. 然后回过头来继续初始化A对象 这时B对象已经完全初始化好了 A实例直接从一级缓存中 把B实例拿出来即可
    7. 到此 完成了A和B实例的循环依赖的初始化

    3、构造器注入搞不定
    因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

    4、prototype 作用域就知道搞不定了

    首先需要知道 什么是prototype? 
    prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

    看描述就知道搞不定了 Spring 初始化完成之后就不管他了 那怎么来解决循环依赖的问题。

    查了几篇博客 看到这个 基本明白了 https://blog.csdn.net/qq_40378034/article/details/104093014
    https://www.cnblogs.com/cocoxu1992/p/10576651.html

    展开全文
  • Spring 循环依赖

    2020-03-30 07:04:06
    1. 什么是循环依赖循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。...Spring循环依赖场景有: (1)构造器的循环依赖 (2)field属性的循环依赖 其中,构造...

    1. 什么是循环依赖?

    循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

    uploading.4e448015.gif正在上传…重新上传取消

    注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。

    Spring中循环依赖场景有: 

    (1)构造器的循环依赖 

    (2)field属性的循环依赖

    其中,构造器的循环依赖问题无法解决,只能拋出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。

    2. 怎么检测是否存在循环依赖

    检测循环依赖相对比较容易,Bean在创建的时候可以给该Bean打标,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。

    3. Spring怎么解决循环依赖

    Spring的循环依赖的理论依据基于Java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的。(但是构造器必须是在获取引用之前)

    Spring的单例对象的初始化主要分为三步: 

    (1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

    (2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

    (3)initializeBean:调用spring xml中的init 方法。

    从上面单例bean的初始化可以知道:循环依赖主要发生在第一、二步,也就是构造器循环依赖和field循环依赖。那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

    这三级缓存分别指: 

    singletonFactories : 单例对象工厂的cache 

    earlySingletonObjects :提前暴光的单例对象的Cache 

    singletonObjects:单例对象的cache

    在创建bean的时候,首先想到的是从cache中获取这个单例的bean,这个缓存就是singletonObjects。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则:从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory。这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

    这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

    知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

    4.基于构造器的循环依赖

    Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

    Spring容器先创建单例A,A依赖B,然后将A放在“当前创建Bean池”中,此时创建B,B依赖C ,然后将B放在“当前创建Bean池”中,此时创建C,C又依赖A, 但是,此时A已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)

    展开全文
  • spring 循环依赖

    2020-06-07 10:48:57
    1.从哪知道spring能支持循环依赖 spring默认支持的,有个属性allowCircularReferences,并且提供api供我们修改,但是如果要生效需要在srping初始化之前就更改好。 例子 两个类 A B 循环依赖 2.循环依赖的过程 ...

    1.从哪知道spring能支持循环依赖

    spring默认支持的,有个属性allowCircularReferences,并且提供api供我们修改,但是如果要生效需要在srping初始化之前就更改好。

    例子

    两个类 A B 循环依赖


    2.循环依赖的过程

    1.初始化扫描出来的类后,会调用getBean()方法去实例化扫描出来的类。之后调用doGetBean()

    2.第一次调用getSingleton() 去一级缓存拿,拿不到

    3.第二次调用getSingleton()  在set集合中记录A正在被创建,然后创建A后,如果支持循环依赖,那就放入二级缓存中。

    4.当进行到属性填充的时候,A发现依赖了B。回去先从一级缓存中拿,如果没有就调用createBean创建B

    5.B和A之前一样的流程

    6.B第一次调用getSingleton() 为空,调用第二次getSingleton() ,同样会在set集合中记录自己正在被创建

    7.B放入二级缓冲中,走到属性填充的时候,发现A正在被创建,

    8.B先从一级缓存拿A,拿不到,从三级缓存拿,拿不到从二级缓冲拿,拿到了

    9.把拿到的A放入三级缓存,移除二级缓冲的A

    (这里为什么要放入三级缓存,其实是为了避免重复创建,因为二级缓存拿对象是执行一个工厂方法的,并不是直接拿对象,如果把工厂方法拿出的对象放进三级缓存中,那就可以避免再次执行工厂方法)

    10.完成B,再接着完成A

     

    为什么二级缓存是工厂对象,其实就是拿到对象之前能执行一些方法,比如完成AOP,我们知道一般的流程中,AOP是在注入属性之后完成的,那在循环引用当中,我们把AOP执行放到了工厂对象,提前完成AOP,如果不提前,B注入A的时候,A是没有AOP的

    展开全文
  • 主要为大家详细介绍了spring循环依赖策略,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Spring循环依赖debug源码图
  • Spring 循环依赖问题

    2021-06-10 09:27:42
    什么是Spring循环依赖? 2、怎么解决Spring容器的循环依赖的问题? 构造器注入 是不能解决Spring容器循环依赖的 Spring官网说明 set 注入可以解决Spring 循环依赖的问题 单纯java代码演示: Spring 容器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,290
精华内容 22,516
关键字:

spring循环依赖

spring 订阅