首先看下Spring创建一个bean的基本流程:
创建该实例的原始对象 --> 进行自动装配 --> AOP代理处理 --> 完成bean的创建并加入单例池(即一级缓存)
但是当有循环依赖的时候,Spring是如何解决的呢?
主要原理是利用三级缓存机制:
singletonObjects: 一级缓存,也就是我们平常理解的单例池。
singletonFactories: 二级缓存,存储的是单例工厂。
earlySingletonObjects: 三级缓存,存储正在创建中的单例对象。
为什么要设立三级缓存呢?
其实当不需要实现AOP的时候,解决循环依赖不用三级缓存机制,也不用单例工厂,二级缓存就足以实现。
第一级:singletonObjects
第二级:earlySingletonObjects
不需要实现AOP时Spring解决循环依赖基本流程:
假设单例一与单例二相互依赖对方并且此时都没有加入到单例池
1 创建单例一
2 将单例一加入earlySingletonObjects缓存
3 自动装配单例二
4 判断单例二在earlySingletonObjects缓存是否存在
5 不存在则创建单例二
6 将单例二并且加入earlySingletonObjects缓存
7 自动装配单例一
8 判断单例一在earlySingletonObjects缓存是否存在
9 明显第2步已经加入earlySingletonObjects缓存
10 注入成功,单例2创建完成并加入singletonObjects单例池
11 注入成功,单例1创建完成并加入singletonObjects单例池
那为什么要设立三级缓存呢?
这是因为当我们需要使用AOP时,将会对原始对象进行代理,因此最后的对象将是代理对象而不是原始对象!
所以按照二级缓存的步骤进行创建的话将会造成一个问题:
第2步加入到二级缓存中的对象是原始对象,导致第7步自动装配到到单例2中的单例1对象是原始对象,这个对象还没有完成AOP的处理。如果此时完成单例2的创建,之后在完成进行单例1的创建时,单例1对原始对象进行AOP处理,将导致最终的单例1对象不是之前自动装配到单例2中的单例1对象。
所以Spring选择使用三级缓存来解决这个问题:singletonFactories缓存中的对象是一个单例工厂,该工厂可以将原始对象进行AOP处理。
singletonObjects: 一级缓存,也就是我们平常理解的单例池。
singletonFactories: 二级缓存,存储的是单例工厂。
earlySingletonObjects: 三级缓存,存储正在创建中的单例对象。
获取三级缓存的核心代码如下:
getSingleton方法的大概处理过程为:
1 判断singletonObjects单例池中是否存在,存在则返回
2 不存在则判断earlySingletonObjects缓存中是否存在,存在则返回
3 不存在则判断singletonFactories缓存中是否存在,不存在则返回null
4 存在则通过该存储工厂创建出最终的bean
5 将该bean加入earlySingletonObjects缓存并从singletonFactories缓存中中移除
存在三级缓存机制下处理流程如下:
假设单例1与单例2相互依赖对方并且此时都没有加入到单例池
1 创建单例一
2 创建单例一的工厂对象并且加入singletonFactories缓存
3 自动装配单例二
4 通过 getSingleton方法 判断单例二在三级缓存中是否存在
5 明显此时单例一不存在,所以创建单例二
6 创建单例二的工厂并且加入singletonFactories缓存
7 自动装配单例一
8 通过 getSingleton方法 判断单例一在三级缓存中是否存在
9 明显第2步已经加入singletonFactories缓存
10 单例二进行AOP处理
11 注入单例一成功,单例二创建完成并加入singletonObjects单例池
12 注入单例二成功,单例一创建完成并加入singletonObjects单例池
以上步骤Spring就完美解决了循环依赖的问题!