浅谈Spring三级缓存及循环依赖

浅谈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类创建也完毕了,流程图如下:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值