循环依赖就是在多个bean中,相互持有对方,导致在创建的时候无法加载。如:beanA引用了beanB,beanB又引用了beanC,beanC最后又引用回了beanA,成了一个无限的循环。循环依赖是对象与对象之间才会发生的,而方法之间的相互调用的情况,叫做循环调用,此招无解最终会因为方法之间调用过多导致内存溢出。
循环依赖分类
实际spring将循环依赖细分为了三种,并且spring只能解决在setter下的循环依赖,而无法解决其他两种情况。
- 构造器循环依赖: 通过构造方法注入bean时发生的
- setter循环依赖:通过setter方法
- prototype作用域循环依赖
三级缓存
- singletonObjects:完整的单例对象的缓存,即bean依赖也注入过的
- singletonFactories : 仅实例化完成后的,返回的是单例对象工厂对象缓存,并未组装依赖关系
- earlySingletonObjects :提前暴光的单例对象的缓存,注入过程中的对象
源码分析
- 通过配置文件或@Bean,配合ComponentScan生成BeanDefinition
- 通过反射实例化bean
- 组装依赖关系,一个关键方法pupulateBean,会从BeanFactory中查找要依赖的bean,如果不存在,就会先去实例化,体现了懒加载的思想
自动解决循环依赖
- A的创建: A a = new A();
- 属性注入: 发现需要B
- 创建B b=new B();
- 属性注入: 发现需要A,此时A已经创建了.只是还没经过各种后置处理器处理,所以B是可以完成属性注入的,只是一个半成品
- 之后回到第2步,就可以给A赋值了,循环依赖到此解决
@Lazy注解原理
- A的创建: A a=new A();
- 属性注入: 发现需要B,查询字段b的所有注解,发现有@lazy注解,那么就不直接创建B了,而是使用动态代理创建一个代理类B
- 此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A