循环依赖

循环依赖其实就是循环引用,即两个或者两个以上的bean互相持有对方,最终形成闭环Spring循环依赖.png

两大场景

image.png

第一种:构造器注入循环依赖

  1. @Service
  2. public class AService {
  3. public AService(BService bService) { }
  4. }
  5. @Service
  6. public class BService {
  7. public BService(AService aService) { }
  8. }

结果:

  1. The dependencies of some of the beans in the application context form a cycle:
  2. ┌─────┐
  3. | AService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\AService.class]
  4. | BService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\BService.class]
  5. └─────┘

Spring解决循环依赖依靠的是Bean的中间态,而这个中间态是指已经实例化,但未初始化。构造器执行是在中间态之前,因此构造器的循环依赖无法解决。

第二种:单例的setter注入

  1. @Service
  2. public class AService {
  3. @Autowired
  4. private BService bService;
  5. }
  6. @Service
  7. public class BService {
  8. @Autowired
  9. private AService aService;
  10. }

这种场景经常使用,没有问题。

第三种:多例的setter注入

  1. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  2. @Service
  3. public class AService {
  4. @Autowired
  5. private BService bService;
  6. }
  7. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  8. @Service
  9. public class BService {
  10. @Autowired
  11. private AService aService;
  12. }
  13. @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
  14. @Component
  15. public class CService {
  16. @Autowired
  17. private AService aService;
  18. }

结果:

  1. The dependencies of some of the beans in the application context form a cycle:
  2. c (field private top.parak.depend.AService top.parak.depend.C.aService)
  3. ┌─────┐
  4. | AService (field private top.parak.depend.BService top.parak.depend.AService.bService)
  5. | BService (field private top.parak.depend.AService top.parak.depend.BService.aService)
  6. └─────┘

没有使用缓存,每次都会生成一个新对象。

总结

  1. 构造器注入和prototype类型的field注入发生循环依赖时都无法初始化
  2. field注入单例的bean时,尽管有循环依赖,但是bean可以成功初始化

如何检测

Bean在创建的时候可以给该Bean打个标志,如果递归调用回来发现正在创建中的话,即说明产生了循环依赖。

如何解决

DefaultSingletonBeanRegistry
内部属性:

  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  2. /** 一级缓存 */
  3. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  4. /** 三级缓存 */
  5. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  6. /** 二级缓存 */
  7. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  8. /** 保存所有已经注册的bean的名字 */
  9. private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
  10. /** 标识指定名字的bean对象是否处于创建状态 */
  11. private final Set<String> singletonsCurrentlyInCreation =
  12. Collections.newSetFromMap(new ConcurrentHashMap<>(16));
  13. // ....
  14. }

三级缓存:

缓存 级别 用途
singletonObjects 一级 存放最终单例,key为bean名称,value为bean实例。这里的bean实例指的是已经完全创建好的,即已经经历实例化->属性填充->初始化->后置处理过程的bean,可以直接使用。
earlySingletonObjects 二级 存放早期对象,key为bean名称,value为bean实例。这里的bean实例指的是仅完成实例化的bean,还未进行属性填充等后续操作。用于提前曝光,供别的bean引用,解决循环依赖。
singletonFactories 三级 存放回调方法,key为bean名称,value为bean工厂。在bean实例化之后,属性填充之前,如果允许提前曝光,Spring会把该bean转换为bean工厂并加入到三级缓存。在需要引用提前曝光对象时再通过工厂对象的getObject方法获取。

源码分析

IOC容器获取bean的入口为AbstractBeanFactorygetBean方法:

  1. public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
  2. @Override
  3. public Object getBean(String name) throws BeansException {
  4. return doGetBean(name, null, null, false);
  5. }
  6. // 真正实现
  7. protected <T> T doGetBean(
  8. String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  9. throws BeansException {
  10. String beanName = transformedBeanName(name);
  11. Object bean;
  12. // 先去获取一次,如果不为null,此处就会走缓存
  13. Object sharedInstance = getSingleton(beanName);
  14. if (sharedInstance != null && args == null) {
  15. // 不为空,则进行后续处理并返回
  16. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  17. } else {
  18. ...
  19. try {
  20. ...
  21. RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  22. ...
  23. // 从三级缓存中没有获取到Bean实例,并且目标Bean是单实例Bean的话
  24. if (mbd.isSingleton()) {
  25. // 通过getSingleton方法创建Bean实例
  26. sharedInstance = getSingleton(beanName, () -> {
  27. try {
  28. // 创建Bean实例
  29. return createBean(beanName, mbd, args);
  30. }
  31. });
  32. // 后续处理,并返回
  33. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  34. }
  35. ...
  36. }
  37. }
  38. return (T) bean;
  39. }
  40. }

首先通过getSingleton(String beanName)方法从三级缓存中获取bean实例,如果不为空则进行后续处理;如果为空,则通过getSingleton(String beanName, ObjectFactory<?) singletonFactory方法创建bean实例并进行后续处理。
从三级缓存中获取bean实例的源码如下:

  1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  2. public Object getSingleton(String beanName) {
  3. return getSingleton(beanName, true);
  4. }
  5. // 这里是经典的DCL(双重检查锁)
  6. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  7. // 从一级缓存中获取bean
  8. Object singletonObject = this.singletonObjects.get(beanName);
  9. // 一级缓存中的bean为空,且当前bean正在创建
  10. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  11. // 加锁
  12. synchronized (this.singletonObjects) {
  13. // 从二级缓存中获取bean
  14. singletonObject = this.earlySingletonObjects.get(beanName);
  15. // 二级缓存中的bean为空,且允许提前创建
  16. if (singletonObject == null && allowEarlyReference) {
  17. // 从三级缓存中获取对应的ObjectFactory
  18. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  19. // ObjectFactory不为空
  20. if (singletonFactory != null) {
  21. // 从单例工厂中获取bean
  22. singletonObject = singletonFactory.getObject();
  23. // 添加到二级缓存
  24. this.earlySingletonObjects.put(beanName, singletonObject);
  25. // 从三级缓存中删除
  26. this.singletonFactories.remove(beanName);
  27. }
  28. }
  29. }
  30. }
  31. return singletonObject;
  32. }
  33. ...
  34. }

主要流程:

  • 首先,尝试从一级缓存中singletonObjects中获取单例Bean
  • 获取不到,则从二级缓存earlySingletonObjects中获取单例Bean
  • 获取不到,则从三级缓存singletonFactories中获取单例ObjectFactory
  • 如果从三级缓存中获取成功,则将ObjectFactory中的object取出放入到二级缓存中,并将ObjectFactory从三级缓存中移除

如果通过三级缓存的查找都没有找到目标bean实例,则通过getSingleton(String beanName, ObjectFactory<?> singleton)方法创建:

  1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  2. ...
  3. // 添加互斥锁
  4. synchronized (this.singletonObjects) {
  5. // 从一级缓存获取
  6. Object singletonObject = this.singletonObjects.get(beanName);
  7. if (singletonObject == null) {
  8. // 为空则继续
  9. ...
  10. // 将当前bean名称添加到正在创建bean的集合singletonsCurrentlyInCreation
  11. beforeSingletonCreation(beanName);
  12. boolean newSingleton = false;
  13. boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  14. ...
  15. try {
  16. // 通过函数式接口创建bean实例,该实例已经经历实例化、属性填充、初始化和后置处理,可直接使用
  17. singletonObject = singletonFactory.getObject();
  18. newSingleton = true;
  19. }
  20. catch (IllegalStateException ex) {
  21. // 判断在此期间是否隐式出现了单例对象
  22. ...
  23. }
  24. finally {
  25. // 将当前bean名称从正在创建的集合singletonsCurrentlyInCreation中移除
  26. afterSingletonCreation(beanName);
  27. }
  28. if (newSingleton) {
  29. // 添加到缓存中
  30. addSingleton(beanName, singletonObject);
  31. }
  32. }
  33. return singletonObject;
  34. }
  35. }
  36. protected void addSingleton(String beanName, Object singletonObject) {
  37. // 加锁
  38. synchronized (this.singletonObjects) {
  39. // 添加到一级缓存
  40. this.singletonObjects.put(beanName, singletonObject);
  41. // 删除对应的二级缓存
  42. this.singletonFactories.remove(beanName);
  43. // 删除对应的三级缓存
  44. this.earlySingletonObjects.remove(beanName);
  45. // 添加到已注册单例
  46. this.registeredSingletons.add(beanName);
  47. }
  48. }

重点关注singletonFactory.getObject()singletonFactory是一个函数式接口,对应AbstractBeanFactorydoGetBean方法中的lambda表达式:

  1. sharedInstance = getSingleton(beanName, () -> {
  2. try {
  3. // 创建bean实例
  4. return createBean(beanName, mbd, args);
  5. }
  6. catch (BeansException ex) {
  7. destroySingleton(beanName);
  8. throw ex;
  9. }
  10. });

重点关注createBean方法,该方法为抽象方法,由AbstractBeanFactory子类AbstractAutowireCapableBeanFactory实现:

  1. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  2. throws BeanCreationException {
  3. try {
  4. Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  5. if (logger.isTraceEnabled()) {
  6. logger.trace("Finished creating instance of bean '" + beanName + "'");
  7. }
  8. return beanInstance;
  9. }
  10. }
  11. protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  12. throws BeanCreationException {
  13. // 实例化bean
  14. BeanWrapper instanceWrapper = null;
  15. ...
  16. Object bean = instanceWrapper.getWrappedInstance();
  17. ...
  18. synchronized (mbd.postProcessingLock) {
  19. if (!mbd.postProcessed) {
  20. try {
  21. // 执行MergedBeanDefinitionPostProcessor类型后置处理器
  22. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  23. }
  24. ...
  25. mbd.postProcessed = true;
  26. }
  27. }
  28. // 如果该bean是单例,并且允许循环依赖的出现以及该bean正在创建中
  29. // 那么就标识允许单例bean提前暴露原始对象引用
  30. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  31. isSingletonCurrentlyInCreation(beanName));
  32. if (earlySingletonExposure) {
  33. // 添加到单实例集合中,即三级缓存对象,该方法第二个参数类型为ObjectFactory函数式接口
  34. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  35. }
  36. // 初始化bean
  37. Object exposedObject = bean;
  38. try {
  39. // 属性赋值
  40. populateBean(beanName, mbd, instanceWrapper);
  41. exposedObject = initializeBean(beanName, exposedObject, mbd);
  42. }
  43. ...
  44. // 允许bean提前暴露
  45. if (earlySingletonExposure) {
  46. // 第二个参数为false标识仅从一级和二级缓存中获取bean单例
  47. Object earlySingletonReference = getSingleton(beanName, false);
  48. if (earlySingletonReference != null) {
  49. if (exposedObject == bean) {
  50. // 如果从一级和二级缓存中获取到bean实例为空
  51. exposedObject = earlySingletonReference;
  52. }
  53. ...
  54. }
  55. }
  56. // Register bean as disposable.
  57. try {
  58. registerDisposableBeanIfNecessary(beanName, bean, mbd);
  59. }
  60. ...
  61. return exposedObject;
  62. }

其中addSingletonFactory方法在父类DefaultSingletonBeanRegistry已实现:

  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2. // 添加互斥锁
  3. synchronized (this.singletonObjects) {
  4. // 如果一级缓存没有,那么添加到三级缓存
  5. if (!this.singletonObjects.containsKey(beanName)) {
  6. this.singletonFactories.put(beanName, singletonFactory);
  7. this.earlySingletonObjects.remove(beanName);
  8. this.registeredSingletons.add(beanName);
  9. }
  10. }
  11. }

整个流程总结如下:
getBean.svg

面试逼问

(1)只用一级缓存是否可以解决循环依赖?
可以解决。

对于一级缓存,我们不等对象初始化完成之后再存入缓存,而是等对象实例化完成后就提前暴露,存入一级缓存,由于此时缓存对象并没有进行初始化操作,即早期对象。那么A实例化完成提前暴露早期对象,A在属性注入时,发现B不在容器中,也没有提前暴露,那么去进行B的生命周期,B实例化后进行属性注入,发现A提前暴露,那么B可以从一级缓存中获取到A的早期对象完成初始化,回到A的生命周期从而A也可以完成初始化。 但是这样会引发另一个问题:早期对象和完整对象都存在一级缓存中,如果此时来了其他线程并发获取bean,就可能从一级缓存中获取到bean的早期对象,这样明显不行,那么我们不得已在从一级缓存获取对象处加一个互斥锁,以避免这个问题。 加互斥锁代理来另一个问题:容器刷新完成后的普通获取bean的请求都需要竞争锁,如果这样处理,在高并发场景下使用Spring的性能必然降低。

(2)加上二级缓存有什么好处?
优化一级循环加锁带来的性能问题。

将对象的早期对象存入二级缓存中,一级缓存用于存储完整对象。 这样B在创建过程中进行属性注入时,先从一级缓存中获取A,获取失败则从二级缓存中获取。 这种获取bean的逻辑也可能出现其他线程获取到早期对象的问题,所以还是要加互斥锁。只不过这里的加锁逻辑可以下沉到二级缓存。那么普通的getBean请求可以直接从一级缓存获取对象,而不用去竞争锁。

(3)二级缓存如何解决AOP问题?
代理对象提前创建。

Spring支持以CGLIB和JDK动态代理的方式为对象创建代理类以提供AOP支持,代理对象的创建通常是在bean初始化完成之后进行(通过BeanPostProcessor后置处理器)。 如果没有循环依赖,那么代理对象依然在初始化完成后创建,如果有循环依赖,那么提前创建代理对象。 如何判断发生了循环依赖? 在B创建的过程中获取A的时候,发现二级缓存中有A,就说明发生了循环依赖,此时就为A创建代理对象,将其覆盖到二级缓存中,并且将代理对象复制给B的对应数字。 当出现多级循环依赖的时候,可以在对象实例化完成之后,将beanName存在一个Set中,标识对应的bean正在创建中,而当其他对象创建的过程依赖某个对象的时候,判断其是否在这个Set中,如果在就说明发生了循环依赖。

(4)那么三级缓存有什么作用?
虽然仅仅依靠二级缓存能够解决循环依赖和AOP的问题,但是从解决方案上来看,维护代理对象的逻辑和getBean的逻辑过于耦合。

三级缓存的key还是为beanName,但是value是一个函数(ObjectFactory#getBean方法),在该函数中执行获取早期对象的逻辑:getEarlyBeanReference方法。 在getEarlyBeanReference方法中,Spring会调用所有SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,通过该方法可以修改早期对象的属性或者替换早期对象。这个是Spring留给开发者的另一个扩展点,虽然我们很少使用,不过在循环依赖遇到AOP的时候,代理对象就是通过这个后置处理器创建。

参考资料

[1] 🕊️ 鸟叔博客:https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.html
[2] 💤 黑哥知乎:https://zhuanlan.zhihu.com/p/375308988