Spring如何解决循环依赖

文章目录

Spring是如何解决循环依赖的问题的

三级缓存,提前暴露对象,aop

总:什么是循环依赖问题,A依赖B,B依赖A

分:先说明bean的创建过程,实例化,初始化(填充属性)

1.先创建A对象,实例化A对象,此时A对象中的b属性为空

2.从容器中查找B对象,如果找到了,直接赋值不存在循环依赖问题,找不到直接创建B对象

3.实例化B对象,此时B对象中的a属性为空,填充属性a

4.从容器中查找A对象,找不到,直接创建

形成闭环原因

此时,会发现A对象是存在的,只不过此时的A对象不是一个完整的状态,只完成了实例化但是未完成初始化,如果在程序调用过程中,拥有了某个对象的引用,能否在后期给他完成赋值操作,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,所以解决问题的核心在于实例化和初始化分开操作,这也是解决循环依赖问题的关键,当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中,此时在容器中存在对象的几个状态,完成实例化但未完成初始化,完整状态,因为都在容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为他们的查找顺序是1,2,3这样的方式来查找的。一级缓存中放的是完整对象,二级缓存中放的是非完整对象

为什么需要三级缓存?三级缓存的value类型是ObjectFactory是一个函数接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。

如果一个对象需要被代理,或者说需要生成代理对象,那么要不要优先生成一个普通对象?要普通对象和代理对象是不能同时出现在容器中的,因此当一个对象需要被代理时,就要使用代理对象覆盖掉之前的普通对象,在实际的调用过程中,是没有办法确定什么时候对象被使用,所以就要求当某个对象被调用的时候,优先判断此对象是否被代理,类似于一种回调机制的实现,因此传入lambda表达式的时候,可以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanReference()

因此所有的bean对象在创建的过程都要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象

缓存的放置时间和删除时间

三级缓存:createBeanInstance之后:addSingletonFactory

二级缓存:第一次从三级缓存确定对象是代理对象还是普通对象的时候,同时删除三级缓存getSingleton

一级缓存:生成完整对象之后放到一级缓存,删除二三级缓存addSingleton
Spring的缓存

第一级缓存存放完全初始化好的Bean,这个Bean可以直接使用了

单例的Bean被创建后就被存放在一级缓存中,其实就是一个Map,只要这个单例bean被创建后,以后再次获取直接从Map中获取

第二级缓存存放原始的Bean对象,也就是说Bean里面的属性还没有进行赋值

第三级缓存存放Bean工厂对象,用来生成原始Bean对象并放入到二级缓存中

singletonFactories,三级缓存,主要存放半成品单例bean的被包装成的ObjectFactory,后续可以解决循环依赖,

单例模式下的setter循环依赖:通过"三级缓存"处理循环依赖,能处理。

一级缓存放成品对象,二级缓存存放半成品对象

Spring本身也考虑到了这方面的问题,所以它设计了三级缓存来解决部分循环依赖的问题。

所谓三级缓存,其实就是用来存放不同类型的Bean。

第一级缓存存放完全初始化好的Bean,这个Bean可以直接使用了

第二级缓存存放原始的Bean对象,也就是说Bean里面的属性还没有进行赋值

第三级缓存存放Bean工厂对象,用来生成原始Bean对象并放入到二级缓存中

假设BeanA和BeanB存在循环依赖,那么在三级缓存的设计下,我画了这样一个图来描述工作原理。

初始化BeanA,先把BeanA实例化,然后把BeanA包装成ObjectFactory对象保存到三级缓存中。

接着BeanA开始对属性BeanB进行依赖注入,于是开始初始化BeanB,同样做两件事,创建BeanB实例,以及加入到三级缓存。

然后,BeanB也开始进行依赖注入,在三级缓存中找到了BeanA,于是完成BeanA的依赖注入BeanB初始化成功以后保存到一级缓存,于是BeanA可以成功拿到BeanB的实例,从而完成正常的依赖注入。

整个流程看起来很复杂,但是它的核心思想就是把Bean的实例化和Bean中属性的依赖注入这两个过程分离出来

如果整个应用程序中不涉及Aop的存在,那么二级缓存中足以解决循环依赖的问题,

如果aop中存在了循环依赖,那么就必须要使用三级缓存才能解决,因为找不到代理对象了,需要用到三级缓存中的lambda表达式生成代理对象

过要注意的是,Spring本身只能解决单实例存在的循环引用问题,但是存在以下四种情况需要人为干预:

多实例的Setter注入导致的循环依赖,需要把Bean改成单例。

构造器注入导致的循环依赖,可以通过@Lazy注解

DependsOn导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖。

单例的代理对象Setter注入导致的循环依赖,可以使用@Lazy注解,或者使用

@DependsOn注解指定加载先后关系。

在实际开发中,出现循环依赖的根本原因还是在代码设计的时候,因为模块的耦合度较高,

依赖关系复杂导致的,我们应该尽可能的从系统设计角度去考虑模块之间的依赖关系,避免循环依赖的问题。

spring循环依赖无法解决的场景

1.多例Bean通过setter注入的情况,不能解决循环依赖问题

2.构造器注入的Bean的情况,不能解决循环依赖问题

3.单例的代理Bean通过Setter注入的情况,不能解决循环依赖问题

4.设置了@DependsOn的Bean的情况,不能解决循环依赖问题

@Lazy注解能解决循环依赖吗?

@Lazy注解不能直接解决循环依赖问题。循环依赖是指两个或多个Bean之间相互依赖,形成了一个循环链。Spring容器默认情况下无法处理循环依赖,会抛出BeanCurrentlyInCreationException异常。

@Lazy注解是用于延迟加载Bean的,它将Bean的初始化过程推迟到首次使用时。虽然使用@Lazy可以解决某些特定情况下的依赖问题,但它本身并不是专门用来解决循环依赖的。

要解决循环依赖问题,Spring提供了多种解决方案:

  1. 构造函数注入:通过在构造函数参数中使用依赖注入,而不是使用字段注入或setter方法注入,可以避免循环依赖。
  2. @Autowired注解配合setter方法:将@Autowired注解放在setter方法上,而不是字段上,也可以解决循环依赖问题。
  3. 使用@PostConstruct注解:使用@PostConstruct注解在Bean创建完成后执行一些初始化操作,可以解决某些循环依赖问题。
  4. 使用代理对象:对于循环依赖中的其中一个Bean,可以使用代理对象来解决。Spring AOP可以帮助生成代理对象,从而解决循环依赖。
    需要注意的是,尽管Spring提供了这些解决方案,但循环依赖本身可能是设计上的问题,应该尽量避免出现循环依赖。在设计和组织Bean之间的依赖关系时,应该考虑到解耦和模块化的原则,以减少循环依赖的出现。
    是的,@Lazy注解可以解决循环依赖问题。
    在Spring框架中,当两个或多个Bean相互依赖时,就可能导致循环依赖问题。在某些情况下,一个Bean A依赖于Bean B,而Bean B又依赖于Bean A,这样就会形成一个循环依赖。
    循环依赖可能会导致各种问题,例如初始化的顺序不确定、资源泄漏等。
    为了解决循环依赖问题,Spring提供了一个@Lazy注解。当在Bean上使用@Lazy注解时,Spring会延迟初始化该Bean,直到真正需要它时才进行初始化。这样就可以避免在Bean初始化时立即创建依赖的Bean,从而解决循环依赖问题。
    例如,假设有两个Bean A和B,其中A依赖于B,B又依赖于A。如果没有使用@Lazy注解,当A初始化时,B也会被初始化,导致循环依赖。但是,如果在Bean B上使用了@Lazy注解,那么当A初始化时,B不会被立即初始化。只有当真正需要B时,例如通过注入到其他Bean中时,B才会被初始化。这样就避免了循环依赖问题。
    需要注意的是,使用@Lazy注解可能会引入一些性能开销,因为Bean的初始化是在真正需要时才进行的。因此,在使用@Lazy注解时需要权衡其带来的利弊。
    过度使用 @Lazy 可能会导致应用程序的行为难以预测和跟踪,特别是在涉及多个依赖和复杂业务逻辑的情况下。
    循环依赖本身通常被认为是设计上的问题。

循环依赖底层源码

1.Spring一级缓存作用

第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象 成员属性都是有值的

2.Spring二级缓存作用

第二级缓存 earlySingletonObjects:存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整)

3.Spring三级缓存作用

第三级缓存 Map<String, ObiectFactory<?>> singletonFactories:存放可以生成Bean的工厂

1.先执行 AService的构造方法----AService实例化 F8 一步一步分析代码如何走的

2.在为我们AService属性填充之前,会将我们的该对象 封装成ObjectFactory存入到三级缓存中。

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			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) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

调用ObjectFactory.getObject()..执行Lambda  执行该方法:getEarlyBeanReference
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;
	}

3.为我们的AService中成员属性赋值,实例化BService 会执行到BService 无参构造方法

    3.1为我们的BService 中成员属性赋值,AService (单例池中查找getBean("AService "))
     3.2 调用getBean("AService ")获取  AService 对象赋值给我们的BService 依赖于AService属性。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    ### 根据beanName 从单例池中获取bean对象
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
                ###从二级缓存中获取bean对象
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
                    // 从三级缓存中获取singletonFactory
					ObjectFactory<?> singletonFactory = this.singletonFactories
                        .get(beanName);
					if (singletonFactory != null) {
                        ##执行三级缓存对应的..执行Lambda  执行该方法:
                            getEarlyBeanReference
                        ## 判断如果开启了aop 则会生成代理类 AService代理类
                        ## 将我们的AService代理类 赋值给我们BService
                        ## 如果没有开启aop的情况下 则直接返回原始不完整bean对象
						singletonObject = singletonFactory.getObject();
                        ###将该代理类存入到二级缓存中 存入不完整对象
						this.earlySingletonObjects.put(beanName, singletonObject);
						###在删除三级缓存
                        this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

    3.3 	singletonObject = singletonFactory.getObject(); 执行
	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;
	}



如果我们开启了aop的情况下 则返回aop代理对象
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

如果我们没有开启aop的情况下 则返回原始对象
@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		return bean;
	}

A依赖B

B依赖A-----a对象存入二级缓存中不完整bean对象

B依赖C

C依赖A ----getBean("a") 直接从二级缓存中查找到的。

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) {

singletonObject = singletonFactory.getObject();

this.earlySingletonObjects.put(beanName, singletonObject);

this.singletonFactories.remove(beanName);

}

}

}

}

1.当我们正在创建A对象时,执行A类中无参构造方法

2.在执行属性赋值之前会先保存一个Lambda表达式在三级缓存中,key 为beanName value为该Lambda表达式

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			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) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

3.在开始执行属性的赋值操作,A对象依赖B对象,会执行B对象的无参构造方法

4.B对象开始执行属性赋值,发现 B对象依赖A对象,但是A对象没有创建成功。

5.则会调用到(getSingleton):

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) {
                    // 二级缓存如果没有该对象 则通过三级缓存中 保存的Lambda 执行 获取Bean对象
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                        //则通过三级缓存中 保存的Lambda 执行 获取Bean对象
						singletonObject = singletonFactory.getObject();
                        //在保存到二级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
singletonObject = singletonFactory.getObject();---重点代码:

6.当我们在调用singletonFactory.getObject(); 本质上在执行三级缓存中保存的

Lambda表达式。

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;
	}

如果我们使用了aop的情况下 ,则会基于aop生成代理类 判断我们的被代理类是否有

实现接口,如果有实现接口则采用jdk动态代理 如果没有实现接口则采用cglib代理。

如果我们没有使用aop的情况下,则返回的就是原始对象。

7.将singletonFactory.getObject(); 返回对象存入到二级缓存中 (代理对象、原始对象)

8...继续执行生命周期后续流程。

circular reference

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ruleGroupAssociationServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ruleGroupServiceImpl': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'ruleGroupAssociationServiceImpl': Requested bean is currently in creation: Is there an unresolvable circular reference?

这是一个Spring框架中的错误。根据错误信息,创建名为'ruleGroupAssociationServiceImpl'的bean时出现了问题,它的资源依赖注入失败。进一步的错误信息显示,创建名为'ruleGroupServiceImpl'的bean时也出现了问题,它的资源依赖注入失败,并且存在一个无法解决的循环引用问题。

循环引用是指两个或多个bean之间相互依赖,形成了一个循环依赖关系。在这种情况下,Spring容器无法解析这种循环引用,导致无法正确创建这些bean。

要解决这个问题,可以考虑以下几种方法:

  1. 检查代码中的循环依赖:检查'ruleGroupAssociationServiceImpl'和'ruleGroupServiceImpl'之间的依赖关系,确保没有循环引用的情况发生。
  2. 使用延迟初始化(Lazy Initialization):将其中一个bean的依赖注入方式改为延迟初始化,以避免循环依赖。可以通过使用@Autowired注解的"required=false"选项或者将依赖注入改为构造函数注入来实现延迟初始化。
  3. 调整bean的定义顺序:重新排列bean的定义顺序,确保先创建的bean不依赖于后创建的bean,从而避免循环依赖。
  4. 使用setter注入代替构造函数注入:如果使用构造函数注入导致循环依赖问题,可以尝试改用setter方法进行依赖注入。
    通过以上方法中的一种或多种,您应该能够解决循环引用的问题。
相关推荐
程序媛小果7 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林12 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨26 分钟前
El表达式和JSTL
java·el
duration~1 小时前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp2 小时前
Java:数据结构-枚举
java·开发语言·数据结构
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong2 小时前
Java反射
java·开发语言·反射
九圣残炎3 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode