精华内容
下载资源
问答
  • java对象循环依赖问题

    千次阅读 2016-08-26 19:42:31
    那么此时不管你实例化哪一个类,都会出现StackOverflowError错误,这就是java对象循环依赖问题。类似于鸡和蛋的问题。 首先我们看下面这段错误的代码,分析其错误的地方。 [java] view plain copy...

    假设我们有一个类A,它包含了一个B的对象,同时类B也包含了一个A对象。那么此时不管你实例化哪一个类,都会出现StackOverflowError错误,这就是java对象循环依赖问题。类似于鸡和蛋的问题。

    首先我们看下面这段错误的代码,分析其错误的地方。

    [java]  view plain  copy
     print ?
    1. public class CyclicDependencies {  
    2.     public static void main(String args[]){  
    3.         Chicken c = new Chicken() ;  
    4.         //Egg e = new Egg() ;  
    5.     }  
    6. }  
    7.   
    8. class Chicken{  
    9.     private Egg e ;  
    10.     private int age ;  
    11.     public Chicken(){  
    12.         e = new Egg() ;  
    13.         setAge(10) ;  
    14.     }  
    15.     public int getAge() {  
    16.         return age;  
    17.     }  
    18.     public void setAge(int age) {  
    19.         this.age = age;  
    20.     }  
    21.   
    22. }  
    23. class Egg{  
    24.     private Chicken chicken ;  
    25.     private int weight ;  
    26.     public Egg(){  
    27.         chicken = new Chicken() ;  
    28.         setWeight(1) ;  
    29.     }  
    30.     public int getWeight() {  
    31.         return weight;  
    32.     }  
    33.     public void setWeight(int weight) {  
    34.         this.weight = weight;  
    35.     }  
    36. }  


    Exception in thread "main" java.lang.StackOverflowError
    

    这是上面代码报的错误,因为当你创建一个Chicken对象时,同时也需要一个Egg对象,而一个Egg对象也需要一个Chicken对象,这样一直循环下去就发生了栈溢出的错误。

    那么如何来解决这个问题呢?我们可以给Chicken写一个代理类ChickenProxy,这样Egg包含的不再是Chicken而是代理类ChickenProxy,这样就利用了第三方来解决循环依赖问题。代码如下。

    [java]  view plain  copy
     print ?
    1. public class CyclicDependencies {  
    2.     public static void main(String args[]){  
    3.         Chicken c = new Chicken() ;  
    4.         Egg e = new Egg(c) ;  
    5.   
    6.         System.out.println(c.getAge());  
    7.         System.out.println(e.getWeight());  
    8.     }  
    9. }  
    10.   
    11. interface ChickenProxy{  
    12.     int getAge();  
    13.     void setAge(int age) ;  
    14. }  
    15.   
    16. class Chicken implements ChickenProxy{  
    17.     private Egg e ;  
    18.     private int age ;  
    19.     public Chicken(){  
    20.         e = new Egg(this) ;  
    21.         setAge(10) ;  
    22.     }  
    23.     public int getAge() {  
    24.         return age;  
    25.     }  
    26.     public void setAge(int age) {  
    27.         this.age = age;  
    28.     }  
    29.   
    30. }  
    31. class Egg{  
    32.     private ChickenProxy chicken ;  
    33.     private int weight ;  
    34.     public Egg(Chicken c){  
    35.         chicken = c ;  
    36.         setWeight(1) ;  
    37.     }  
    38.     public int getWeight() {  
    39.         return weight;  
    40.     }  
    41.     public void setWeight(int weight) {  
    42.         this.weight = weight;  
    43.     }  
    44. }  



    2014-11-13 15:41:41

    Brave,Happy,Thanksgiving !

    展开全文
  • 循环依赖就是N个类相互嵌套引用,如果通过new对象的方式产生循环依赖的话会导致程序内存溢出报错,接下来我们了解一下spring是如何解决循环依赖问题。第一种:prototype原型bean循环依赖对于原型bean的初始化过程中...

     循环依赖就是N个类相互嵌套引用,如果通过new对象的方式产生循环依赖的话会导致程序内存溢出报错,接下来我们了解一下spring是如何解决循环依赖问题。

    第一种:prototype原型bean循环依赖

    对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx方法产生循环依赖,Spring都会直接报错处理。
    AbstractBeanFactory.doGetBean()方法:
    if (isPrototypeCurrentlyInCreation(beanName)) {
    		throw new BeanCurrentlyInCreationException(beanName);
    	}
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    		Object curVal = this.prototypesCurrentlyInCreation.get();
    		return (curVal != null &&
    				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    	}
    在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记
    try {
    		//创建原型bean之前添加标记
    		beforePrototypeCreation(beanName);
    		//创建原型bean
    		prototypeInstance = createBean(beanName, mbd, args);
    	}
    	finally {
    		//创建原型bean之后删除标记
    		afterPrototypeCreation(beanName);
    	}
    总结:简单来说Spring不支持原型bean的循环依赖。

    第二种:单例bean 构造器参数循环依赖

    对于单例bean通过构造器参数进行循环依赖时,Spring也是通过抛出异常进行处理的,接下来我们分析一下其是如何实现。假设这里有两个类ClassA 和 ClassB,两个类通过构造器进行循环依赖。
    1、Spring创建ClassA时首先会调用beforeSingletonCreation(beanName),判断单例bean是否正在被创建,如果正在被创建则报错,如果没有被创建则做标记
    protected void beforeSingletonCreation(String beanName) {
    		//singletonsCurrentlyInCreation是一个集合,不可重复,add返回true则表明没有创建,返回false则说明bean在创建
    		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
    			throw new BeanCurrentlyInCreationException(beanName);
    		}
    	}
    此时刚开始创建ClassA,因此不会报异常错误,当初始化ClassA的构造器时需要用到ClassB的实例。

    2、Spring从容器中无法获取ClassB的实例,接下来Spring按照步骤1创建ClassB的实例,在构造ClassB的构造函数时需要用到ClassA的实例。
    3、Spring还是首先尝试从容器中获取ClassA的实例,由于第1步还没有执行结束,此时还没有ClassA实例,Spring容器认为就需要初始化ClassA了,重复第1步,
    但是ClassA一开始就已经进行了第1步,并且做标记bean正在创建,当再次进入第一步时就会抛出异常错误。

    总结:Spring在创建构造器循环依赖时其实就是循环初始化操作 A-> B -> A  当A要被初始化第二次时就直接抛出异常。

    第三种 :单例bean通过setXxx或者@Autowired进行循环依赖

    Spring通过setXxx或者@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
    1、Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    				isSingletonCurrentlyInCreation(beanName));
    		if (earlySingletonExposure) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Eagerly caching bean '" + beanName +
    						"' to allow for resolving potential circular references");
    			}
    			//将初始化后的对象提前已ObjectFactory对象注入到容器中
    			addSingletonFactory(beanName, new ObjectFactory<Object>() {
    				@Override
    				public Object getObject() throws BeansException {
    					return getEarlyBeanReference(beanName, mbd, bean);
    				}
    			});
    		}
    2、ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。
    3、Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
    4、ClassB调用setClassA方法,Spring从容器中获取ClassA ,因为第一步中已经提前暴露了ClassA,因此可以获取到ClassA实例
    5、ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
    6、这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。


    总结:简单来说就是对象通过构造函数初始化之后就暴露到容器中,这样就不会存在循环初始化对象的情况了。



    展开全文
  • Spring 如何解决循环依赖的问题

    万次阅读 多人点赞 2018-03-31 21:35:03
    (一)Spring IOC容器---对象循环依赖1. 什么是循环依赖? what? (1)循环依赖--&gt;循环引用。---&gt;即2个或以上bean 互相持有对方,最终形成闭环。 eg:A依赖B,B依赖C,C又依赖A。【注意:这里...

    (一)Spring  IOC容器---对象循环依赖

    1. 什么是循环依赖?  what?

       (1)循环依赖-->循环引用。--->即2个或以上bean 互相持有对方,最终形成闭环。

       eg:A依赖B,B依赖C,C又依赖A。【注意:这里不是函数的循环调用是个死循环,除非有终结条件】,是对象相互依赖关系

    2.  Spring中循环依赖的场景?where?

         ①:构造器的循环依赖。【这个Spring解决不了

    StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况,

    我们都把这三个Bean交给Spring管理,并用有参构造实例化

    1. public class StudentA {  
    2.   
    3.     private StudentB studentB ;  
    4.   
    5.     public void setStudentB(StudentB studentB) {  
    6.         this.studentB = studentB;  
    7.     }  
    8.   
    9.     public StudentA() {  
    10.     }  
    11.       
    12.     public StudentA(StudentB studentB) {  
    13.         this.studentB = studentB;  
    14.     }  
    15. }  
    [java]  view plain  copy
    1. public class StudentB {  
    2.   
    3.     private StudentC studentC ;  
    4.   
    5.     public void setStudentC(StudentC studentC) {  
    6.         this.studentC = studentC;  
    7.     }  
    8.       
    9.     public StudentB() {  
    10.     }  
    11.   
    12.     public StudentB(StudentC studentC) {  
    13.         this.studentC = studentC;  
    14.     }  
    15. }  
    [java]  view plain  copy
    1. public class StudentC {  
    2.   
    3.     private StudentA studentA ;  
    4.   
    5.     public void setStudentA(StudentA studentA) {  
    6.         this.studentA = studentA;  
    7.     }  
    8.   
    9.     public StudentC() {  
    10.     }  
    11.    
    12.     public StudentC(StudentA studentA) {  
    13.         this.studentA = studentA;  
    14.     }  
    [html]  view plain  copy
    1. <bean id="a" class="com.zfx.student.StudentA">  
    2.     <constructor-arg index="0" ref="b"></constructor-arg>  
    3. </bean>  
    4. <bean id="b" class="com.zfx.student.StudentB">  
    5.     <constructor-arg index="0" ref="c"></constructor-arg>  
    6. </bean>  
    7. <bean id="c" class="com.zfx.student.StudentC">  
    8.     <constructor-arg index="0" ref="a"></constructor-arg>  
    9. </bean>   

    下面是测试类:

    [java]  view plain  copy
    1. public class Test {  
    2.     public static void main(String[] args) {  
    3.         ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");  
    4.         //System.out.println(context.getBean("a", StudentA.class));  
    5.     }  
    6. }  
    执行结果报错信息为:
    [java]  view plain  copy
    1. Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:   
    2.     Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?  

         ②【setter循环依赖】field属性的循环依赖【setter方式 单例,默认方式-->通过递归方法找出当前Bean所依赖的Bean,然后提前缓存【会放入Cach中】起来。通过提前暴露 -->暴露一个exposedObject用于返回提前暴露的Bean。】

    setter方式注入:


    图中前两步骤得知:Spring是先将Bean对象实例化【依赖无参构造函数】--->再设置对象属性的

     这就不会报错了:

    原因:Spring先用构造器实例化Bean对象----->将实例化结束的对象放到一个Map中,并且Spring提供获取这个未设置属性的实例化对象的引用方法。结合我们的实例来看,,当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题喽。

    3.  如何检测是否有循环依赖?how to  find?

       可以 Bean创建的时候给其打个标记,如果递归调用回来发现正在创建中的话--->即可说明循环依赖。

       

    4.怎么解决的?  todo what?

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

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


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

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

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

    从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖field循环依赖。

    那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存

      

    调整配置文件,将构造函数注入方式改为 属性注入方式 即可

    3.源码怎么实现的? how?

     (1)三级缓存源码主要 指:

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    这三级缓存分别指:

     singletonFactories : 单例对象工厂的cache 
     earlySingletonObjects :提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥

     singletonObjects:单例对象的cache

    我们在创建bean的时候,首先想到的是从cache中获取这个单例的bean,这个缓存就是singletonObjects。主要调用方法就就是:

    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) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

    上面的代码需要解释两个参数:

    • isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。)
    • allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象

    分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则:

    this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
    • 1
    • 2

    从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义如下:

    public interface ObjectFactory<T> {
        T getObject() throws BeansException;
    }
    • 1
    • 2
    • 3

    这个接口在下面被引用

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里就是解决循环依赖的关键,这段代码发生在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三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决


    展开全文
  • Spring-bean的循环依赖以及解决方式

    万次阅读 多人点赞 2017-09-12 08:18:21
    本文主要是分析Spring bean的循环依赖,以及Spring的解决方式。 通过这种解决方式,我们可以应用在我们实际开发项目中。 什么是循环依赖? 怎么检测循环依赖 Spring怎么解决循环依赖 Spring对于循环依赖无法...

    本文主要是分析Spring bean的循环依赖,以及Spring的解决方式。 通过这种解决方式,我们可以应用在我们实际开发项目中。

    1. 什么是循环依赖?
    2. 怎么检测循环依赖
    3. Spring怎么解决循环依赖
    4. Spring对于循环依赖无法解决的场景
    5. Spring解决循环依赖的方式我们能够学到什么?

    1. 什么是循环依赖?

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

    这里写图片描述

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

    Spring中循环依赖场景有:
    (1)构造器的循环依赖
    (2)field属性的循环依赖。

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

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

    3. Spring怎么解决循环依赖

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

    Spring的单例对象的初始化主要分为三步:
    bean初始化
    (1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

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

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

    从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二部。也就是构造器循环依赖和field循环依赖。

    那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存

    首先我们看源码,三级缓存主要指:

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    这三级缓存分别指:
    singletonFactories : 单例对象工厂的cache
    earlySingletonObjects :提前暴光的单例对象的Cache
    singletonObjects:单例对象的cache

    我们在创建bean的时候,首先想到的是从cache中获取这个单例的bean,这个缓存就是singletonObjects。主要调用方法就就是:

    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) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

    上面的代码需要解释两个参数:

    • isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。)
    • allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象

    分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,如果获取到了则:

    this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);

    从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义如下:

    public interface ObjectFactory<T> {
        T getObject() throws BeansException;
    }

    这个接口在下面被引用

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

    这里就是解决循环依赖的关键,这段代码发生在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三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

    参考 http://www.jianshu.com/p/6c359768b1dc

    展开全文
  • Spring如何解决循环依赖问题

    万次阅读 多人点赞 2020-12-09 02:23:10
    1、什么是循环依赖:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。 2、循环依赖问题在Spring中主要有三种情况: (1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行...
  • spring循环依赖

    2019-01-18 11:25:26
    Spring循环依赖的三种方式 依赖场景:A-&gt;B-&gt;A 或者 A-&gt;B-&gt;C-&gt;A1.理解spring循环依赖的原理,首先了解一下Spring bean在容器中创建到销毁的若干阶段。 单例bean循环依赖体现在...
  • 利用原生循环依赖关系序列化复杂JavaScript对象或ES6类
  • spring循环依赖问题

    2018-10-27 22:03:19
    循环依赖就是循环引用,就是两个或多个bean之间相互持有对象,比如A引用B,B引用C,C引用A,他们最终反映为一个环,如下类图所示:   spring如何解决循环依赖 spring容器的循环依赖分为两类:一类是构造器循环...
  • 循环依赖问题

    2018-09-25 20:26:09
    循环依赖的产生和解决的前提 循环依赖的产生可能有很多种情况,例如: A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象 A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A...
  • 什么是循环依赖 最近学习了一下spring的循环依赖问题,这里总结一下。首先,我们应该知道什么是spring的依赖问题,想要明白这个问题,我们就要先明白spring的创建bean的过程。这里我简单说一下,spring在启动的时候会...
  • Spring循环依赖

    万次阅读 2017-03-31 19:40:06
    Spring循环依赖 A与B中的A是否是同一对象
  • Spring IOC 循环依赖

    2018-06-24 00:11:11
    一、什么是循环依赖?...}二、循环依赖的类别1、构造器循环依赖(此依赖问题无法解决,会发生栈溢出)2、set循环依赖(只能解决单例循环依赖问题)三、set循环依赖的解决办法class A { B b; public B ...
  • 循环依赖 循环依赖(区别于dependsOn)的现象很简单,A对象在创建的时候依赖于B对象,而B对象在创建的时候依赖于A对象
  • 浅谈Spring的循环依赖

    千次阅读 2020-05-29 16:50:29
    A,所以这样子就产生了对象循环依赖。 基于构造器的循环依赖 先上基于构造器的循环依赖例子: @Component public class A { private B b; public A(B b) { this.b = b; } } @Component public class B { ...
  • Spring解决bean的循环依赖

    千次阅读 2019-09-28 15:54:08
    什么是循环依赖循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实...
  • spring 循环依赖注入

    万次阅读 2017-09-28 13:43:48
    什么是循环依赖 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环。 spring 中循环依赖注入分三种情况 1. 构造器循环依赖 2. setter方法循环...
  • Spring循环依赖及解决方式

    千次阅读 2020-01-27 15:16:52
    一、什么是循环依赖 ...Spring的循环依赖的理论依据基于Java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的(但是构造器必须是在获取引用之前) Spring的单例对象的初始化主要分为三步: c...
  • 其他网址 Spring源码-循环依赖 - 草捏子的博客 | ChayCao Blog
  • 如果 class A 中依赖了 class B并且class B 中也依赖了class A,形成一个闭环就会产生循环依赖的问题。 解决 构造器注入方式的循环依赖,无法解决; Setter注入方式的循环依赖,解决方式: Spring先用构造器实例化...
  • 序列化本地化具有循环依赖项的复杂JavaScript对象或ES6类的序列化。 重要警告此本地插件使用的基础v8 API仍处于试验阶段,并且二进制兼容串行化将具有循环依赖关系的复杂JavaScript对象或ES6类本地化为本地序列。 ...
  • 循环依赖 循环依赖、循环调用 循环依赖是针对成员变量---单例才可以解决setter方法循环依赖,多例是无法解决循环依赖 构造方法循环依赖----无法解决,只有将构造依赖改为setter方法依赖 setter方法循环依赖---...
  • Spring解决循环依赖的方法

    万次阅读 多人点赞 2019-07-25 00:21:15
    从逻辑上说明Spring是怎么解决循环依赖的,然后从源码上看Spring是怎么做的。
  • spring如何解决循环依赖问题?

    千次阅读 多人点赞 2021-03-01 15:49:35
    循环依赖其实就是循环引用,很多地方都说需要两个或则两个以上的bean互相持有对方最终形成闭环才是循环依赖,比如A依赖于B,B依赖于C,C又依赖于A。其实一个bean持有自己类型的属性也会产生循环依赖。 setter ...
  • SpringBean循环依赖

    2020-04-11 14:44:21
    SpringBean循环依赖什么是循环依赖循环依赖的方式循环依赖的解决prototype(多例)singleton(单例) 啥也不说先来一波时序图 什么是循环依赖 循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对...
  • 在基于Jackson类库将对象转化为json字符串时,如果对象中存在互相依赖,则会产生无限循环的情况,具体情况如下: BuyerEntity.java @Table(name="t_buyer") @Entity @ToString(callSuper=true) @Data @...
  • Spring源码剖析-Spring如何处理循环依赖

    千次阅读 多人点赞 2021-06-13 16:16:53
    这是最近较为频繁被问到的一个面试题,在前面Bean实例化流程中,对属性注入一文多多少少对循环依赖有过介绍,这篇文章详细讲一下Spring中的循环依赖的处理方案。 什么是循环依赖 依赖指的是Bean与Bean之间的依赖关系...
  • 灵魂画手图解Spring循环依赖

    万次阅读 多人点赞 2020-03-14 11:48:08
    想彻底弄清楚spring的循环依赖问题,首先得弄清楚1. 循环依赖是如何发生的,spring又是如何检测循环依赖的发生的。其次才是2. 探究spring如何解决循环依赖的问题 1. 循环依赖检查 <bean id="a" class="A"> &...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 270,472
精华内容 108,188
关键字:

对象循环依赖