1.构造器参数循环依赖 :
通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyIn CreationException异常表示循环依赖。
如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建TestC,最终在创建TestC时发现又需要TestA,从而形成一个环,没办法创建。
Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean
池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
2.setter方式单例,默认方式 :
Spring先是用构造实例化Bean对象 ,此时Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法。当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题。
3.prototype作用域bean的循环依赖 :
这种循环依赖同样无法解决,因为spring不会缓存prototype作用域的bean,而spring中循环依赖的解决方式正是通过缓存来实现的。
Spring解决这个问题主要靠巧妙的三层缓存,。 Spring首先从singleTonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingleTonObjects(二级缓存)
中获取,如果还是获取不到并且允许从singleTonFactories通过getObject获取,则通过singleTonFactory.getObject()(三级缓存)获取。
如果获取到了则移除相对应的singleTonFactory,将singleTonObject放入到earlySingleTonObjects,其实就是将三级缓存提升到二级缓存,这个就是缓存升级。Spring在进行对象创建的时候,会依次从一级、二级、三级缓存中寻找对象,如果找到直接返回。
拓展:
一级缓存:singletonObjects,存放完全实例化属性赋值完成的单例对象的cache,直接可以使用。
二级缓存:earlySingletonObjects,存放提前曝光的单例对象的cache,尚未进行属性封装的Bean。
三级缓存:singletonFactories,存放单例对象工厂的cache。