循环依赖就是循环引用 ,指两个或者多个bean互相持有依赖对方,比如A引用B, B引用C,C引用A,最终形成一个闭环
补充: 注意区别于循环调用;
什么是循环调用?
指方法之间的环调用,循环调用是无解的,除非有终结条件,否则就是死循环,最终会导致内存溢出异常
表示通过构造器注入造成的循环依赖,此依赖是无解的,强行依赖只能抛出异常BeanCreationException;
bean的创建过程:
spring容器将每一个正在创建的bean标识符放在一个当前创建的bean池中,bean标识符在创建过程中将一直保持在这个池中,因此在创建bean的过程中如果发现自己已经在池中,则抛出BeanCreationException异常表示循环依赖,而对于创建完成的bean将从 当前创建bean池中清除掉;
出现依赖错误的原因:
Spring容器先创建单例A,A依赖B,然后将A放在“当前创建Bean池”中,此时创建B, B依赖C,然后将B放在“当前创建Bean池”中,此时创建C,C又依赖A, 但是,此时A已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)
指通过setter注入方式构成的循环依赖。
spring实例化bean流程图:
spring的解决办法:
Spring先用构造器实例化Bean对象----->将实例化结束的对象放到一个Map中,并且Spring提供获取这个未设置属性的实例化对象的引用方法。结合我们的实例来看,,当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,就不会出来循环的问题。
错误原因:
scope=“prototype” 意思是 每次请求都会创建一个实例对象。
两者的区别是:有状态的bean都使用Prototype作用域,无状态的一般都使用singleton单例作用域。
对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring
容器不进行缓存,因此无法提前暴露一个创建中的Bean。
三级缓存如下:
/** 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);
名词解释:
singletonObjects :指单例对象的cache
singletonFactories: 指单例对象工厂的cache
earlySingletonObjects:指提前曝光的单例对象的cache
举例解释三级缓存的实际应用:
如A依赖B,B依赖C的场景:
A完成初始化第一步,并且将自己提前曝光到singletonFactories中,
此时进行初始化的第二步,发现自己依赖对象B,就去get(B),发现B还没有被初始化,所以走初始化流
程,
B在初始化第一步发现自己依赖于A,于是尝试get(A),
尝试一级缓存singletonObjects(A还没有完全初始化),
尝试二级缓存earlySingletonObjects(也没有),
尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能通过ObjectFactory.getObject拿到A对象(尚未完全初始化的状态),
B拿到A对象后顺利完成了初始化阶段1,2,3,完全初始化之后将自己放入到一级缓存singleObjects中,
此时返回A,A此时能拿到B的对象顺利完成自己的初始化阶段2,3,最终A也完成了自己的初始化阶段,放入到一级缓存singletonObjects中;
Spring通过三级缓存加上“提前曝光”机制,配合Java的对象引用原理,比较完美地解决了某些情况下的循环依赖问题!
参考1
参考2