循环依赖
- 构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异 常表示循环依赖。
- 基于 field 属性的循环依赖,也要分两种细分的情况来对待:
- scope 为 singleton 的循环依赖。 Spring通过三级缓存去解决
- scope 为 prototype 的循环依赖。Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。
spring三级缓存
spring内部有三级缓存:
- 成品:一级缓存 singletonObjects ,用于保存实例化、注入、初始化完成的bean实例 :
- 半成品:二级缓存 earlySingletonObjects ,用于保存实例化完成的bean实例
- 原材料工厂: 三级缓存 singletonFactories ,用于保存bean的创建工厂,以便于后面扩展有机会创建代理对象。
- 三级缓存是singletonFactories ,但是是不完整的Bean的工厂Factory,是当一个Bean在new之后 (没有属性填充、初始化),就put进去。所以,是原材料工厂
- 二级缓存是对三级缓存的过渡性处理,只要通过 getObject() 方法从三级缓存的BeanFactory中 取出Bean一次,原材料就变成变成品,就put到二级缓存,所以,二级缓存里边的bean,都是半成品
- 一级缓存里面是完整的Bean,是当一个Bean完全创建后(完成属性填充、彻底的完成初始化)才put进去, 所以是成品
为什么要有三级缓存,二级缓存不行嘛?
TestService1依赖于TestService2和TestService3, 而TestService2依赖于TestService1,同时TestService3也依赖于TestService1。
假设不用二级缓存,TestService1注入到TestService3需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是 ObjectFactory 对象,而通过它创建的实例对象每次可能都不一样的。 第三级缓存中为什么要添加 ObjectFactory 对象,直接保存实例对象不行吗?不行,因为假如你想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通的。
使用三级缓存,主要是为了生成代理对象。 因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。 使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。 假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,在 BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致。