浅谈Spring三级缓存及循环依赖
前言
提到Spring加载流程,其中有一个核心不得不提到,便是Spring的三级缓存机制,其主要用来解决循环依赖的问题,那么何谓循环依赖呢,可以参考以下代码:
public class A{
@Autowired
private B b;
}
public class B{
@Autowired
private A a;
}
这里类A依赖了类B,而类B又依赖了类A,那么在Bean加载的过程中便形成了循环依赖。
介绍
三级缓存核心容器
关于三级缓存的实现,最最核心的容器如下
// 一级缓存
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
// 二级缓存
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
// 三级缓存
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
这里按顺序分别为1、2、3级缓存所用到的核心容器,通过注释能清楚的知道其存储的的数据是什么,其中三级缓存容器singletonFactories
比较特别,这里并不直接存储bean实例,而是存储了ObjectFactory
,而ObjectFactory
的实现方法getObject()
主要用于获取bean实例。
这三个核心容器存储的数据用意也不同,主要有以下区别
*一级缓存singletonObjects
该容器内存储的Bean经历了完整的Spring Bean初始化生命周期,从该缓存容器中获取的Bean能直接使用
*二级缓存earlySingletonObjects
该容器内存储的Bean已经被创建,但是尚未完成Spring初始化生命周期
*三级缓存singletonFactories
该容器内主要存储了尚未完成初始化的Bean对应的beanFactory,看到这里可能会不解这里和二级缓存的区别,其实这里正正就是三级缓存机制的核心,通俗的讲,如果有些Bean类需要创建代理的话,将会在这里通过Bean工厂的getObject()方法将原Bean引用转换为代理Bean的引用,如果Spring不需要使用AOP的话,使用二级缓存即可解决循环依赖的问题
单例Bean的获取
了解完三级缓存关键容器,下面结合Spring单例Bean获取逻辑来讲解这三个缓存容器的作用。浅显的讲,当获取单例Bean时,会先从一级缓存中找,找不到再找二级缓存,再找不到则查找三级缓存中是否有该类的相关信息,如果再找不到则返回null,流程图如下:
核心代码如下:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存中bean不存在 且 该单例bean状态为正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存中bean不存在 且 该bean允许提前暴露
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 三级缓存中存在该bean的beanFactory
if (singletonFactory != null) {
// 调用工厂方法获取Bean
singletonObject = singletonFactory.getObject();
// 将该Bean加入二级缓存,注意这里的Bean未完成初始化的生命周期
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中移除该Bean的beanFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
这里可能讲的比较浅显,一些状态记录及判断没有细讲,关于三级缓存及相关实例获取的核心代码在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
,有兴趣可以去了解一下
提前暴露
接下来就是解决循环依赖最核心的点,就是提前暴露
1、关于提前暴露的描述这里沿用前言中的例子,当Spring创建A类时,首先完成A类实例化,然后把A类相关信息放到第三级缓存中。
2、紧接着对A类进行成员属性填充,此时发现需要填充B类,而第一、二、三级缓存中都不存在B类的实例及工厂信息,因此需要去对B类进行创建。
3、同第一步一样,B类实例化后,属性填充时也会发现需要填充A类的实例,此时B类能从第三级缓存中获取A类的实例,因此B类完成加载。
4、此时回到第二步,B类创建完毕后A类也能获得B类的实例了,因此A类创建也完毕了,流程图如下: