方案导入

循环依赖是什么

构造出两个对象A和B,A中有成员B,B中有成员A,换成代码就是这样子。

@Component
 
 public class A {
 
     @Autowired
 
     private B b;
 
 }
 
  
 
 @Component
 
 public class B {
 
     @Autowired
 
     private A a;
 
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

循环依赖会带来什么问题

如果创建A对象,那必须注入B对象,注入B对象又需要创建A对象,如此反复,直到OOM

什么三级缓存解决了循环依赖问题?别被骗了,一级缓存就够了!_Spring循环依赖

如何解决循环依赖问题

我们想象有这样一幅有向图,我们从A开始,到D结束,当执行到D的时候,D还是会继续执行,因为D不知道A是否被经过。

什么三级缓存解决了循环依赖问题?别被骗了,一级缓存就够了!_Spring循环依赖_02

为了解决这一问题,我们只需要将经过的路径点染色就好了。

什么三级缓存解决了循环依赖问题?别被骗了,一级缓存就够了!_Spring循环依赖_03

D发现A已经是红色,知道已经被途径过,主动结束循环。

我们很容易就能发现,循环依赖问题和图路径中是否有环问题是一样的,就能保证Bean(实例)不被重复创建。

什么三级缓存解决了循环依赖问题?别被骗了,一级缓存就够了!_Spring循环依赖_04

联系Spring

都说Spring三级缓存解决了循环依赖问题,那我们就使用了一级缓存就解决了缓存依赖问题,spring的开发团队怎么会傻到用三级缓存解决问题,当然这句话可能还有一个歧义,第三层缓存区解决了缓存依赖问题,这同样也是错的,且听下文分析。

三级缓存是什么

//  存储单例的Bean对象
 
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
 //  存储早期曝光的单例Bean对象,只是一个Bean的引用,未初始化
 
 private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
 
 //  存储单例Bean对象的工厂
 
 private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

三级缓存如何工作 - 源码部分

我会对关键代码打注释,你这么聪明肯定一下就看懂了

当我们获取想要注入的单例时,有以下代码,删去了 try - catch的异常处理和解决并发问题的代码块,便于清晰的阅读

public Object getSingleton(String beanName) {
 
 	return getSingleton(beanName, true);
 
 }
 
 	
 
 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 
     //从一级缓存中获取Bean
 
     Object singletonObject = this.singletonObjects.get(beanName);
 
     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
 
 		//从二级缓存中获取Bean
 
         singletonObject = this.earlySingletonObjects.get(beanName);
 
         if (singletonObject == null && allowEarlyReference) {
 
 		    //从三级缓存(工厂)中获取Bean
 
             ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
 
             if (singletonFactory != null) {
 
                 singletonObject = singletonFactory.getObject();
 
                 //从工厂中获取到 singletonObject 时,从工厂缓存中删去工厂,工厂创建的对象加入二级缓存
 
                 if (this.singletonFactories.remove(beanName) != null) {
 
                     this.earlySingletonObjects.put(beanName, singletonObject);
 
                 }
 
             }
 
         }
 
     }
 
     return singletonObject;
 
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.

可见 getSingleton 的路径是 一级缓存 → 二级缓存 → 三级缓存,同时当从三级缓存中获取到早期对象时,直接放入二级缓存,删除三级缓存(后续的多次引用也是二级缓存),可见二级缓存+短暂的三级缓存相当于标记bean为已实例化,所以依赖三级缓存解决循环依赖显然是错的

那三级缓存(工厂)到底存储着什么,不是二级缓存就能解决问题了吗?我们在 doCreateBean 中可以看到以下代码。

// 当前 Bean(例如 A)在实例化后、依赖注入和初始化完成前,是否需要将其作为“早引用”暴露给其他 Bean
 
 		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
 
 				isSingletonCurrentlyInCreation(beanName));
 
 		// 允许被早引用(早期曝光)
 
 		if (earlySingletonExposure) {
 
 			// 添加到单例工厂(三级缓存)
 
 			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 
 		}
 
 		
 
 		// 被三级缓存添加后再进行初始化
 
 		Object exposedObject = bean;
 
 		populateBean(beanName, mbd, instanceWrapper);
 
 		exposedObject = initializeBean(beanName, exposedObject, mbd);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

证明了三级缓存以及二级缓存中的对象是引用对象,未被真正初始化,等于是一个懒加载,同样也不会造成循环依赖,因为其内部的对象没有只引用了实例化的对象,未被初始化。

getEarlyBeanReference 应该有关于Factory中的一些信息

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
 
 		Object exposedObject = bean;
 
 		// 是否被代理(AOP,字节码增强)
 
 		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
 
 			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
 
 				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
 
 			}
 
 		}
 
 		return exposedObject;
 
 	}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

倘若没有经过字节码增强代码可以缩略成两行

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
 
 		Object exposedObject = bean;
 
 		return exposedObject;
 
 	}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

诶卧槽,这不是啥也没干!

三级缓存只参与了AOP对象的返回,解决bean的AOP代理问题

下图展示了bean的初始化过程

什么三级缓存解决了循环依赖问题?别被骗了,一级缓存就够了!_Spring循环依赖_05

那我们现在可以得出以下结论了:

  1. 第一级缓存用来简单返回缓存后的bean对象。
  2. 第二级缓存就可以解决循环依赖问题。
  3. 二三级缓存只保留了实例化的bean,未初始化,不会导致循环依赖。
  4. 第三级缓存用来解决Bean的代理类的实例化问题。

如果这篇文章对你有所帮助,请给我点个赞。你的肯定会让我非常开心。🥰🥰🥰