关于Java Spring三级缓存和循环依赖的深入理解

目录
  • 一、什么是循环依赖?什么是三级缓存?
  • 二、三级缓存如何解决循环依赖?
  • 三、使用二级缓存能不能解决循环依赖?

一、什么是循环依赖?什么是三级缓存?

【什么是循环依赖】什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象。通过构建函数创建B对象的实例化。又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃。

【三级缓存】三级缓存实际上就是三个Map对象,从存放对象的顺序开始

三级缓存singletonFactories存放ObjectFactory,传入的是匿名内部类,ObjectFactory.getObject() 方法最终会调用getEarlyBeanReference()进行处理,返回创建bean实例化的lambda表达式。
二级缓存earlySingletonObjects存放bean,保存半成品bean实例,当对象需要被AOP切面代时,保存代理bean的实例beanProxy
一级缓存(单例池)singletonObjects存放完整的bean实例

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

二、三级缓存如何解决循环依赖?

【如何解决循环依赖】Spring解决循环依赖的核心思想在于提前曝光,首先创建实例化A,并在三级缓存singletonFactories中保存实例化A的lambda表达式以便获取A实例,当我没有循环依赖和AOP时,这个三级缓存singletonFactories是没用在后续用到的。
但是当我A对象需要注入B对象,发现缓存里还没有B对象,创建B对象并又上述所说添加进三级缓存singletonFactories,B对象需要注入A对象,这时从半成品缓存里取到半成品对象A,通过缓存的lambda表达式创建A实例对象,并放到二级缓存earlySingletonObjects中。
此时B对象可以注入A对象实例和初始化自己,之后将完成品B对象放入完成品缓存singletonObjects。但是当有aop时,B对象还没有把完成品B对象放入完成品缓存singletonObjects中,B对象初始化后需要进行代理对象的创建,此时需要从singletonFactories获取bean实例对象,进行createProxy创建代理类操作,这是会把proxy&B放入二级缓存earlySingletonObjects中。这时候才会把完整的B对象放入完成品一级缓存也叫单例池singletonObjects中,返回给A对象。
A对象继续注入其他属性和初始化,之后将完成品A对象放入完成品缓存。

三、使用二级缓存能不能解决循环依赖?

一定是不行,我们只保留二级缓存有两个可能性保留一二singletonObjects和earlySingletonObjects,或者一三singletonObjects和singletonFactories

【只保留一二singletonObjects和earlySingletonObjects】

流程可以这样走:实例化A ->将半成品的A放入earlySingletonObjects中 ->填充A的属性时发现取不到B->实例化B->将半成品的B放入earlySingletonObjects中->从earlySingletonObjects中取出A填充B的属性->将成品B放入singletonObjects,并从earlySingletonObjects中删除B->将B填充到A的属性中->将成品A放入singletonObjects并删除earlySingletonObjects。

这样的流程是线程安全的,不过如果A上加个切面(AOP),这种做法就没法满足需求了,因为earlySingletonObjects中存放的都是原始对象,而我们需要注入的其实是A的代理对象。

【只保留一三singletonObjects和singletonFactories】

流程是这样的:实例化A ->创建A的对象工厂并放入singletonFactories中 ->填充A的属性时发现取不到B->实例化B->创建B的对象工厂并放入singletonFactories中->从singletonFactories中获取A的对象工厂并获取A填充到B中->将成品B放入singletonObjects,并从singletonFactories中删除B的对象工厂->将B填充到A的属性中->将成品A放入singletonObjects并删除A的对象工厂。

同样,这样的流程也适用于普通的IOC已经有并发的场景,但如果A上加个切面(AOP)的话,这种情况也无法满足需求。

因为拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象

所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。

getSingleton()、getEarlyBeanReference() 源码如下

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName); // 先从一级缓存拿
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName); // 拿二级缓存
				if (singletonObject == null && allowEarlyReference) {
                    // 拿三级缓存
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                        // 最终会调用传入的匿名内部类getEarlyBeanReference()方法,这里面没调用一次会生成一个新的代理对象
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

到此这篇关于关于Java Spring三级缓存和循环依赖的深入理解的文章就介绍到这了,更多相关Java Spring 三级缓存 循环依赖内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBean依赖和三级缓存的案例讲解

    spring中的bean依赖有大体上可以分为两类,共3中形式,下面简单介绍一下. 第一类是构造方法中的循环依赖,这种会报错 @Service public class ServiceA { private ServiceB serviceB; public ServiceA(ServiceB serviceB) { this.serviceB = serviceB; } public void methodA(){ System.out.println("a"); } } @Servi

  • Spring使用三级缓存解决循环依赖的问题

    Spring如何使用三级缓存解决循环依赖在没开始文章之前首先来了解一下什么是循环依赖 @Component public class A { @Autowired B b; } @Component public class B { @Autowired A a; } 在对象A创建过程中,需要注入B,因为容器中没有B,则去创建B,B创建过程中又需要注入A,而A在等待B的创建,B在等待A的创建,导致两者都无法创建成功,无法加入到单例池供用户使用. Spring则通过三级缓存来解决循环依赖的问题,另

  • 聊聊Spring循环依赖三级缓存是否可以减少为二级缓存的情况

    基于Spring-5.1.5.RELEASE 问题 都知道Spring通过三级缓存来解决循环依赖的问题.但是是不是必须三级缓存才能解决,二级缓存不能解决吗? 要分析是不是可以去掉其中一级缓存,就先过一遍Spring是如何通过三级缓存来解决循环依赖的. 循环依赖 所谓的循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环.比如"A对象依赖B对象,而B对象也依赖A对象",或者"A对象依赖B对象,B对象依赖C对象,C对象依赖A对象":类似以下代码: publ

  • Spring为何要用三级缓存来解决循环依赖问题

    我们都知道Spring为了解决循环依赖使用了三级缓存 Spring三级缓存 一级缓存singletonObjects 用于保存BeanName和创建bean实例之间的关系,beanName -> bean instance private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); 二级缓存earlySingletonObjects 保存提前曝光的单例bean对象 private fin

  • 关于Java Spring三级缓存和循环依赖的深入理解

    目录 一.什么是循环依赖?什么是三级缓存? 二.三级缓存如何解决循环依赖? 三.使用二级缓存能不能解决循环依赖? 一.什么是循环依赖?什么是三级缓存? [什么是循环依赖]什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象.通过构建函数创建B对象的实例化.又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃.

  • 一篇文章带你理解Java Spring三级缓存和循环依赖

    目录 一.什么是循环依赖?什么是三级缓存 二.三级缓存如何解决循环依赖? 三.使用二级缓存能不能解决循环依赖? 总结 一.什么是循环依赖?什么是三级缓存 [什么是循环依赖]什么是循环依赖很好理解,当我们代码中出现,形如BeanA类中依赖注入BeanB类,BeanB类依赖注入A类时,在IOC过程中creaBean实例化A之后,发现并不能直接initbeanA对象,需要注入B对象,发现对象池里还没有B对象.通过构建函数创建B对象的实例化.又因B对象需要注入A对象,发现对象池里还没有A对象,就会套娃.

  • Spring三级缓存解决循环依赖

    目录 一级缓存 为什么不能在实例化A之后就放入Map? 二级缓存 二级缓存已然解决了循环依赖问题,为什么还需要三级缓存? 三级缓存 源码 我们都知道Spring中的BeanFactory是一个IOC容器,负责创建Bean和缓存一些单例的Bean对象,以供项目运行过程中使用. 创建Bean的大概的过程: 实例化Bean对象,为Bean对象在内存中分配空间,各属性赋值为默认值 初始化Bean对象,为Bean对象填充属性 将Bean放入缓存 首先,容器为了缓存这些单例的Bean需要一个数据结构来存储,

  • Spring为何需要三级缓存解决循环依赖详解

    目录 前言 bean生命周期 三级缓存解决循环依赖 总结 前言 在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决循环依赖问题,对我们开发者来说是无感知的,下面具体分析一下spring是如何解决bean之间循环依赖,为什么要使用到三级缓存,而不是二级缓存 bean生命周期 首先大家需要了解一下bean在spring中的生命周期,bean在spring的加载流程,才能够更加清晰知道spring是如何解决循环依赖的 我们在spring的BeanFacto

  • 教你Spring如何使用三级缓存解决循环依赖

    一级缓存存放实例化对象 .二级缓存存放已经在内存空间创建好但是还没有给属性赋值的对象.三级缓存存放对象工厂,用来创建提前暴露到bean的对象. @Service public class TestService1 { @Autowired private TestService2 testService2; public void test1() { } } @Service public class TestService2 { @Autowired private TestService1

  • 你知道怎么用Spring的三级缓存解决循环依赖吗

    目录 1. 前言 2. Spring Bean的循环依赖 3. Spring中三大循环依赖场景演示 3.1 构造器注入循环依赖 3.2 singleton模式field属性注入循环依赖 3.3 prototype模式field属性注入循环依赖 4. Spring解决循环依赖的原理分析 4.1 Spring创建Bean的流程 4.2 Spring容器的“三级缓存” 4.3 源码解析 4.4 流程总结 5. 总结 1. 前言 循环依赖:就是N个类循环(嵌套)引用. 通俗的讲就是N个Bean互相引用对

  • Spring源码之循环依赖之三级缓存详解

    目录 循环依赖 定义 三种循环依赖的情况 1.构造器循环依赖 2.settler循环依赖 3.prototype范围的依赖处理 三级缓存机制 整体分析 源码分析 面试题 总结 循环依赖 定义 循环依赖就 循环引用,就是两个或多个 bean 相互之间的持有对方,比如 CircleA 引用 CircleB , CircleB 引用 CircleC, CircleC 引用 CircleA ,则它们最终反映为 个环.此处不是循环调用,循环调用是方法之间的环调用. 循环调用是无法解决的,除非有终结条件,否

  • Spring Bean创建和循环依赖

    目录 1 前言 2 Bean 的创建 createBeanInstance populateBean initializeBean 循环依赖问题 总结 1 前言 前文已经讲述了Spring BeanFactory 与 FactoryBean 的区别详情,在本文中将继续讲解 Bean 的创建和初始化,在这个环节中将会涉及到 Bean 的创建.初始化和循环依赖内容. 2 Bean 的创建 在前文中已经讲述了 Spring 容器启动的核心方法 refresh,关于 Bean 的创建和初始化方法都是在 

随机推荐