spring如何解决循环依赖

简介: Spring框架处理循环依赖分为构造器循环依赖与setter循环依赖两种情况。构造器循环依赖不可解决,Spring会在检测到此类依赖时抛出`BeanCurrentlyInCreationException`异常。setter循环依赖则通过缓存机制解决:利用三级缓存系统,其中一级缓存`singletonObjects`存放已完成的单例Bean;二级缓存`earlySingletonObjects`存放实例化但未完成属性注入的Bean;三级缓存`singletonFactories`存放创建这些半成品Bean的工厂。

循环依赖

spring中将循环依赖处理分为了两种情况

构造器循环依赖

使用构造器注入构成循环依赖,这种方式无法进行解决,抛出了BeanCurrentlyInCreationException异常

在创建bean之前会进行检测

java

代码解读

复制代码

protected void beforeSingletonCreation(String beanName) {
  // inCreationCheckExclusions中是否存在当前正在创建的bean
  // 并且singletonsCurrentlyInCreation是否可以添加成功(也就是singletonsCurrentlyInCreation中是否存在正在创建的bean)
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

在创建bean之后会进行移除

java

代码解读

复制代码

protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}

以TestA和TestB为例,由于构造器造成了循环依赖,所以在进行实例化TestA时(实例化TestA之前执行了beforeSingletonCreation方法,此时singletonsCurrentlyInCreation中添加TestA),需要TestB实例,所以需要实例化TestB(实例化TestB之前执行了beforeSingletonCreation方法,此时singletonsCurrentlyInCreation中添加TestB),然后在进行TestB时需要TestA实例,又需要进行实例化TestA,但是执行beforeSingletonCreation方法时,singletonsCurrentlyInCreation中已存在TestA,所以会抛出BeanCurrentlyInCreationException异常

java

代码解读

复制代码

public class TestA {
    
    private TestB testB;
    
    public TestA(TestB testB){
        this.testB = testB;
    }
}

public class TestB {
    private TestA testA;

    public TestB(TestA testA){
        this.testA = testA;
    }
}

setter循环依赖

对于setter注入造成的循环依赖,spring采用的是提前暴露刚完成的构造器实例化但未完成setter方法注入的bean来实现的,而且只能解决单例作用域的bean

java

代码解读

复制代码

// ①doGetBean
// 获取bean
// allowEarlyReference是true,表示允许早期依赖
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // 从singletonObjects缓存中取
  // singletonObjects中存的是beanName和bean实例
  // singletonObjects是一级缓存
   Object singletonObject = this.singletonObjects.get(beanName);
  // 缓存中没有
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     // 锁定全局变量进行处理
      synchronized (this.singletonObjects) {
        // 如果该bean正在进行加载则不进行处理,直接返回
        // earlySingletonObjects中存的是beanName和bean实例,但是与singletonObjects不同的是,存储的是还没进行属性注入操作的Bean的实例,,目的是为了监测循环引用
        // earlySingletonObjects是二级缓存
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
           // 从singletonFactories中获取,对于某些方法需要提前进行初始化时则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存在singletonFactories中
           // singletonFactories中存的是beanName和创建bean的ObjectFactory工厂
           // singletonFactories是三级缓存
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
              // 调用ObjectFactory的getObject方法
               singletonObject = singletonFactory.getObject();
              // 放入到二级缓存中
              // 记录在缓存earlySingletonObjects中,earlySingletonObjects和singletonFactories互斥,earlySingletonObjects与singletonObjects的不同之处在于当一个单例bean被放入该缓存后,那么其他的bean在创建过程中就能通过getBean方法获取到,目的是用来监测循环引用
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

// ②doGetBean
// 第一次创建,缓存中不存在,则会进行创建
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
  @Override
  public Object getObject() throws BeansException {
    try {
      // 创建bean
      return createBean(beanName, mbd, args);
    }
    catch (BeansException ex) {
      // Explicitly remove instance from singleton cache: It might have been put there
      // eagerly by the creation process, to allow for circular reference resolution.
      // Also remove any beans that received a temporary reference to the bean.
      destroySingleton(beanName);
      throw ex;
    }
  }
});

// ③doCreateBean
if (instanceWrapper == null) {
  // 创建bean实例
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}

// ④
addSingletonFactory(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					return getEarlyBeanReference(beanName, mbd, bean);
				}
});

// 将ObjectFactory加入到三级缓存中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}
// ⑤
// 调用applyPropertyValues(beanName, mbd, bw, pvs)来set值
populateBean(beanName, mbd, instanceWrapper);

// ⑥
// 整个bean实例创建并且属性注入后执行
// if (newSingleton) {
//					addSingleton(beanName, singletonObject);
//				}
protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
      // 加入一级缓存
			this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
			this.singletonFactories.remove(beanName);
      // 移除二级缓存
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

以TestA和TestB为例,创建单例TestA时,使用无参构造器进行创建,并将实例化的bean放入一个ObjectFactory中,存入到singletonFactories,用于返回提前暴露的bean,然后进行setter方法来注入TestB,此时还没有TestB,来创建单例TestB,使用无参构造器进行创建,并将实例化的bean放入一个ObjectFactory中,存入到singletonFactories,用于返回提前暴露的bean,然后进行setter来注入TestA,从ObjectFactory.getObject可以获取到testA,完成了TestB#setTestA方法,之后继续走TestA的逻辑,从而完成TestA#setTestB方法

java

代码解读

复制代码

public class TestA {
    
    private TestB testB;

    public TestB getTestB() {
        return testB;
    }

    public void setTestB(TestB testB) {
        this.testB = testB;
    }
}

public class TestB {
    private TestA testA;

    public TestA getTestA() {
        return testA;
    }

    public void setTestA(TestA testA) {
        this.testA = testA;
    }
}

总结一下

  • 一级缓存  singletonObjects  存放的是完全创建好的单例Bean
  • 二级缓存  earlySingletonObjects  存放的是完成实例化,但是还未进行属性注入的对象
  • 三级缓存  singletonFactories  提前暴露的一个单例工厂,二级缓存的对象就是通过这个单例工厂创建的

有个疑问

看上去好像二级缓存就可以解决循环依赖了,三级缓存的对象工厂的意义是什么呢?

来看一下不管有没有循环依赖,都会创建好一个对象,然后放入到三级缓存中

java

代码解读

复制代码

// 加入三级缓存
addSingletonFactory(beanName, new ObjectFactory<Object>() {
   @Override
   public Object getObject() throws BeansException {
      return getEarlyBeanReference(beanName, mbd, bean);
   }
});

只有调用三级缓存中的ObjectFactory.getObject()才会拿到对象,看一下getEarlyBeanReference干了什么吧

java

代码解读

复制代码

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
           // 使用后置处理器,这里做了个代理,也就是AOP  (AbstractAutoProxyCreator继承的SmartInstantiationAwareBeanPostProcessor,也就是在getEarlyBeanReference方法中调用的wrapIfNecessary方法)
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            if (exposedObject == null) {
               return null;
            }
         }
      }
   }
   return exposedObject;
}

也就是做了个延迟代理的作用,在二级缓存里存放的其实是代理过的对象


本文转载自:https://juejin.cn/post/7364409181053222938

相关文章
|
1月前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
21天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
3月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
232 24
|
2月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
72 1
|
3月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
46 4
|
4月前
|
存储 缓存 Java
面试问Spring循环依赖?今天通过代码调试让你记住
该文章讨论了Spring框架中循环依赖的概念,并通过代码示例帮助读者理解这一概念。
面试问Spring循环依赖?今天通过代码调试让你记住
|
4月前
|
Java Spring 容器
循环依赖难破解?Spring Boot神秘武器@RequiredArgsConstructor与@Lazy大显神通!
【8月更文挑战第29天】在Spring Boot应用中,循环依赖是一个常见问题。当两个或多个Bean相互依赖形成闭环时,Spring容器会陷入死循环。本文通过对比@RequiredArgsConstructor和@Lazy注解,探讨它们如何解决循环依赖问题。**@RequiredArgsConstructor**:通过Lombok生成包含final字段的构造函数,优先通过构造函数注入依赖,简化代码但可能导致构造函数复杂。**@Lazy**:延迟Bean的初始化,直到首次使用,打破创建顺序依赖,增加灵活性但可能影响性能。根据具体场景选择合适方案可有效解决循环依赖问题。
170 0
|
5月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
5月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
5月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决