循环依赖
循环引用
循环依赖:是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用
Spring 循环依赖有四种:
- DependsOn 依赖加载【无法解决】(两种 Map)
- 原型模式 Prototype 循环依赖【无法解决】(正在创建集合)
- 单例 Bean 循环依赖:构造参数产生依赖【无法解决】(正在创建集合,getSingleton() 逻辑中)
- 单例 Bean 循环依赖:setter 产生依赖【可以解决】
解决循环依赖:提前引用,提前暴露创建中的 Bean
- Spring 先实例化 A,拿到 A 的构造方法反射创建出来 A 的早期实例对象,这个对象被包装成 ObjectFactory 对象,放入三级缓存
- 处理 A 的依赖数据,检查发现 A 依赖 B 对象,所以 Spring 就会去根据 B 类型到容器中去 getBean(B),这里产生递归
- 拿到 B 的构造方法,进行反射创建出来 B 的早期实例对象,也会把 B 包装成 ObjectFactory 对象,放到三级缓存,处理 B 的依赖数据,检查发现 B 依赖了 A 对象,然后 Spring 就会去根据 A 类型到容器中去 getBean(A.class)
- 这时从三级缓存中获取到 A 的早期对象进入属性填充
循环依赖的三级缓存:
//一级缓存:存放所有初始化完成单实例 bean,单例池,key是beanName,value是对应的单实例对象引用
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存:存放实例化未进行初始化的 Bean,提前引用池
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. 3*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
-
为什么需要三级缓存?
- 循环依赖解决需要提前引用动态代理对象,AOP 动态代理是在 Bean 初始化后的后置处理中进行,这时的 bean 已经是成品对象。因为需要提前进行动态代理,三级缓存的 ObjectFactory 提前产生需要代理的对象,把提前引用放入二级缓存
- 如果只有二级缓存,提前引用就直接放入了一级缓存,然后 Bean 初始化完成后又会放入一级缓存,产生数据覆盖,导致提前引用的对象和一级缓存中的并不是同一个对象
- 一级缓存只能存放完整的单实例,为了保证 Bean 的生命周期不被破坏,不能将未初始化的 Bean 暴露到一级缓存
- 若存在循环依赖,后置处理不创建代理对象,真正创建代理对象的过程是在 getBean(B) 的阶段中
-
三级缓存一定会创建提前引用吗?
- 出现循环依赖就会去三级缓存获取提前引用,不出现就不会,走正常的逻辑,创建完成直接放入一级缓存
- 存在循环依赖,就创建代理对象放入二级缓存,如果没有增强方法就返回 createBeanInstance 创建的实例,因为 addSingletonFactory 参数中传入了实例化的 Bean,在 singletonFactory.getObject() 中返回给 singletonObject,所以存在循环依赖就一定会使用工厂,但是不一定创建的是代理对象,不需要增强就是原始对象
-
wrapIfNecessary 一定创建代理对象吗?(AOP 动态代理部分有源码解析)
- 存在增强器会创建动态代理,不需要增强就不需要创建动态代理对象
- 存在循环依赖会提前增强,初始化后不需要增强
-
什么时候将 Bean 的引用提前暴露给第三级缓存的 ObjectFactory 持有?
-
实例化之后,依赖注入之前
createBeanInstance -> addSingletonFactory -> populateBean
-
源码解析
假如 A 依赖 B,B 依赖 A
-
当 A 创建实例后填充属性前,执行:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))// 添加给定的单例工厂以构建指定的单例 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { // 单例池包含该Bean说明已经创建完成,不需要循环依赖 if (!this.singletonObjects.containsKey(beanName)) { //加入三级缓存 this.singletonFactories.put(beanName,singletonFactory); this.earlySingletonObjects.remove(beanName); // 从二级缓存移除,因为三个Map中都是一个对象,不能同时存在! this.registeredSingletons.add(beanName); } } } -
填充属性时 A 依赖 B,这时需要 getBean(B),也会把 B 的工厂放入三级缓存,接着 B 填充属性时发现依赖 A,去进行**第一次 ** getSingleton(A)
public Object getSingleton(String beanName) { return getSingleton(beanName, true);//为true代表允许拿到早期引用。 } protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 在一级缓存中获取 beanName 对应的单实例对象。 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) { // 从三级缓存获取工厂对象,并得到 bean 的提前引用 singletonObject = singletonFactory.getObject(); // 【缓存升级】,放入二级缓存,提前引用池 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存移除该对象 this.singletonFactories.remove(beanName); } } } } return singletonObject; } -
从三级缓存获取 A 的 Bean:
singletonFactory.getObject(),调用了 lambda 表达式的 getEarlyBeanReference 方法:public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 【向提前引用代理池 earlyProxyReferences 中添加该 Bean,防止对象被重新代理】 this.earlyProxyReferences.put(cacheKey, bean); // 创建代理对象,createProxy return wrapIfNecessary(bean, beanName, cacheKey); } -
B 填充了 A 的提前引用后会继续初始化直到完成,返回原始 A 的逻辑继续执行
本文正在参加「金石计划 . 瓜分6万现金大奖」