springboot升级出现循环依赖问题

背景

问题从spring boot 2.3.12升级到2.6.15版本后,项目启动后访问报错.

The dependencies of some of the beans in the application context form a cycle.

serviceCollectionIdCacheService

┌─────┐

| serviceProductInfoProviderImpl

↑ ↓

| serviceOfflineProviderImpl

↑ ↓

| serviceProductMappingProviderImpl

└─────┘

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

原因

在2.6.0之前,spring会自动处理循环依赖的问题,2.6.0 以后的版本默认禁止 Bean 之间的循环引用,如果存在循环引用就会启动失败报错。

解决

方案1

清理循环引用的Bean

1、在字段上使用@Autowired注解,让Spring决定在合适的时机注入。

2、在@Autowired注解上方加上@Lazy注解(延迟加载)

(A—>B—>C—>D 一般在D引用A的@Autowired下加入@Lazy注解即可)

方案2

it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

也可以暂时跳过,在yml配置中加入

spring:
  main:
    allow-circular-references: true

方案3

在启动中加入也一样

public static void main(String[] args) {
  SpringApplication sa = new SpringApplication(xx.class);
  sa.setAllowCircularReferences(Boolean.TRUE);//加入的参数
  sa.run(args);
}

本人采用了方案3来解决问题。

<think>嗯,用户问的是如何解决Spring Boot中的循环依赖问题。首先,我需要回忆一下什么是循环依赖,以及为什么在Spring Boot中会出现这个问题循环依赖指的是两个或多个Bean相互依赖,比如Bean A依赖Bean B,而Bean B又反过来依赖Bean A。当Spring容器在初始化这些Bean的时候,会因为无法确定创建顺序而导致错误。 接下来,我应该考虑Spring处理Bean注入的方式,主要是构造器注入和Setter注入。可能用户在使用构造器注入时更容易遇到循环依赖问题,因为Spring在创建Bean时必须先解析所有构造器参数,而如果存在循环,容器可能无法解决。而Setter注入是在Bean实例化之后进行的,所以可能更容易处理这种情况。 然后,想到Spring Boot从2.6版本开始默认禁止了循环依赖,这可能也是用户遇到问题的原因之一。所以,解决方案之一可能是调整配置,允许循环依赖,但这只是临时的解决办法,毕竟最佳实践是避免循环依赖的设计。 接下来,具体的解决方法可能包括: 1. 重新设计代码结构,消除循环依赖。比如引入第三个类,把共同依赖的部分抽离出来,或者使用接口抽象。这部分需要用户有一定的设计能力,可能需要调整类之间的关系。 2. 使用@Lazy注解。在其中一个依赖上添加@Lazy,延迟加载,这样Spring在初始化的时候可以先创建一个代理,等真正需要的时候再注入实际的Bean。这样可以打破初始化时的循环。 3. 改用Setter注入而不是构造器注入。因为Setter注入允许Bean在实例化之后再进行依赖注入,这样Spring可以处理循环的情况。但可能需要注意方法是否被正确调用。 4. 调整Spring Boot的配置,允许循环依赖。比如在application.properties中设置spring.main.allow-circular-references=true。不过这种方法可能只是掩盖问题,而不是根本解决。 然后,我需要考虑用户的实际场景。比如用户可能是在升级Spring Boot版本后遇到这个问题,因为之前版本可能允许循环依赖,而新版本默认禁止。这时候用户可能需要快速解决问题,所以提供配置选项可能是一个快速解决方案,但同时也要提醒他们最好还是重构代码。 另外,用户可能对循环依赖的原因不太清楚,所以需要解释为什么会出现这个问题,以及各种解决方法的适用场景。例如,使用@Lazy可能在某些情况下有效,但过度使用可能导致其他问题,比如代理对象的异常。 可能还需要提到Spring的三级缓存机制如何处理循环依赖,这样用户可以更深入理解。例如,对于Setter注入,Spring通过提前暴露半成品的Bean来解决循环依赖,而构造器注入无法做到这一点,因为构造器需要完整的依赖。 最后,总结时应该强调避免循环依赖的重要性,尽管有临时解决办法,但良好的设计才是根本。同时,根据具体情况选择最合适的解决方案,比如在紧急情况下允许配置,但后续还是要重构代码。</think>在 Spring Boot 中,**循环依赖**(Circular Dependency)是指两个或多个 Bean 相互依赖,形成闭环,导致 Spring 容器无法正确初始化这些 Bean。以下是解决循环依赖问题的常见方法: --- ### 一、循环依赖的产生场景 例如:`BeanA` 依赖 `BeanB`,而 `BeanB` 又依赖 `BeanA`。 ```java // BeanA @Component public class BeanA { private final BeanB beanB; public BeanA(BeanB beanB) { this.beanB = beanB; } // 构造器注入 } // BeanB @Component public class BeanB { private final BeanA beanA; public BeanB(BeanA beanA) { this.beanA = beanA; } // 构造器注入 } ``` 此时 Spring 会抛出 `BeanCurrentlyInCreationException`。 --- ### 二、解决方法 #### 1. **重构代码(推荐)** - **最佳实践**是避免循环依赖,通过重新设计代码结构: - 提取公共逻辑到第三个类。 - 使用接口或抽象类解耦。 - 使用事件驱动(如 `ApplicationEvent`)替代直接依赖。 #### 2. **使用 `@Lazy` 延迟加载** - 在其中一个 Bean 的依赖上添加 `@Lazy`,告知 Spring 延迟初始化该 Bean: ```java @Component public class BeanA { private final BeanB beanB; public BeanA(@Lazy BeanB beanB) { this.beanB = beanB; // 延迟注入 } } ``` #### 3. **改用 Setter/Field 注入** - **构造器注入**容易导致循环依赖,改用 Setter 或 Field 注入: ```java @Component public class BeanA { private BeanB beanB; @Autowired // Setter 注入 public void setBeanB(BeanB beanB) { this.beanB = beanB; } } ``` #### 4. **配置允许循环依赖(临时方案)** - 在 `application.properties` 中启用循环依赖Spring Boot 2.6+ 默认禁用): ```properties spring.main.allow-circular-references=true ``` - **注意**:这只是绕过问题,不推荐长期使用。 #### 5. **使用 `@PostConstruct`** - 通过 `@PostConstruct` 在 Bean 初始化后手动注入依赖: ```java @Component public class BeanA { private BeanB beanB; @Autowired public void setBeanB(BeanB beanB) { this.beanB = beanB; } } @Component public class BeanB { private BeanA beanA; @Autowired public void setBeanA(BeanA beanA) { this.beanA = beanA; } @PostConstruct public void init() { beanA.setBeanB(this); // 手动关联 } } ``` --- ### 三、Spring 如何解决循环依赖Spring 通过 **三级缓存** 处理单例 Bean 的循环依赖: 1. **一级缓存**(`singletonObjects`):存放完全初始化的 Bean。 2. **二级缓存**(`earlySingletonObjects`):存放提前暴露的未完成初始化的 Bean。 3. **三级缓存**(`singletonFactories`):存放 Bean 的工厂对象,用于生成提前暴露的 Bean。 当检测到循环依赖时,Spring 会提前暴露一个“半成品” Bean,完成后续注入。 --- ### 四、注意事项 1. **构造器注入**无法通过三级缓存解决循环依赖,优先使用 Setter/Field 注入。 2. 尽量避免循环依赖,它是代码设计上的“坏味道”。 3. 若使用 `@Lazy`,需注意代理对象可能引发的潜在问题(如 `NullPointerException`)。 通过合理设计或上述方法,可以有效解决 Spring Boot 中的循环依赖问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值