Spring Boot的循环依赖问题

本文介绍了Spring框架中循环依赖的概念,以及通过构造器方法注入、@Lazy注解和@DependsOn注解来解决循环依赖问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.循环依赖的概念

2.解决循环依赖的方法

1.构造器方法注入:

2.@Lazy注解

3.@DependsOn注解 


1.循环依赖的概念

        两个或多个bean之间互相依赖,形成循环,此时,Spring容器无法确定先实例化哪个bean,导致循环依赖的问题。

代码示例:

@Service
public class A{

    private B b;
    
    @Autowired
    public A(B b){
        this.b = b;
    }

}

@Service
public class B{

    private A a;

    @Autowired
    public B(A a){
        this.a = a;
    }

}

可以看到代码中,A的对象创建需要先实例化B的对象,而B的对象也需要先实例化A,此时形成了循环依赖。

2.解决循环依赖的方法

1.构造器方法注入:

在构造器中注入,解决循环依赖问题

public class Main {
    public static void main(String[] args) {
        ClassB classB = new ClassB(null);
        ClassA classA = new ClassA(classB);
        classB.setClassA(classA);
    }
}

2.@Lazy注解

@Lazy注解具有延迟加载的功能,能够在需要该bean时再进行实例化

@Service
@Lazy
public class A{

    private B b;
    
    @Autowired
    public A(B b){
        this.b = b;
    }

}

@Service
@Lazy
public class B{

    private A a;

    @Autowired
    public B(A a){
        this.a = a;
    }

}

3.@DependsOn注解 

@DependsOn注解能够指定依赖项的加载顺序,从而避免循环依赖问题

如下代码中:@DependsOn("B")指定了A依赖于B,在实例化A之前,必须先实例化B,避免了循环依赖问题

@Service
@DependsOn("B")
public class A{

    private B b;
    
    @Autowired
    public A(B b){
        this.b = b;
    }

}

@Service
@Lazy
public class B{

    private A a;

    @Autowired
    public B(A a){
        this.a = a;
    }

}
Spring Boot中,循环依赖是一个常见但棘手的问题。当两个或多个Bean相互依赖时,就会形成一个依赖环,导致应用启动失败,并抛出类似 `The dependencies of some of the beans in the application context form a cycle` 的错误。 --- ### 三级缓存机制:Spring解决循环依赖的核心策略 Spring通过**三级缓存机制**来处理部分类型的循环依赖问题。其核心思想是将Bean的创建过程拆分为**实例化**和**初始化**两个阶段,并在适当的时候提前暴露一个“早期引用”(Early Reference),供其他Bean引用,从而打破循环。 - **一级缓存**(singletonObjects):存放完全初始化好的Bean。 - **二级缓存**(earlySingletonObjects):存放提前暴露的早期Bean(尚未完成属性填充和初始化)。 - **三级缓存**(singletonFactories):存放用于生成早期Bean的工厂对象。 这种机制适用于**基于setter方法注入**的场景,但在使用**构造器注入**时,Spring无法提前暴露未完全初始化的Bean,因此会直接抛出循环依赖异常[^2]。 --- ### 实战解决方案汇总 #### 1. 使用 `@Lazy` 注解延迟加载 这是最简单且有效的临时解决方案之一。通过在某个依赖项上添加 `@Lazy` 注解,可以延迟该Bean的加载时机,从而避免在初始化阶段出现循环依赖。 ```java @Service public class UserService { @Lazy @Autowired private RoleService roleService; } ``` 此方式适用于单边依赖的场景,能够有效绕过Spring Boot 2.6+版本中默认开启的循环依赖检查[^3]。 --- #### 2. 改用Setter注入代替构造器注入 由于Spring的三级缓存机制仅支持Setter注入下的循环依赖处理,建议在存在循环依赖风险的组件中避免使用构造器注入。 ```java @Service public class UserService { private RoleService roleService; @Autowired public void setRoleService(RoleService roleService) { this.roleService = roleService; } } ``` 这种方式虽然牺牲了构造器注入的不可变性和更清晰的依赖关系表达,但能有效规避循环依赖问题[^1]。 --- #### 3. 拆分Bean职责,重构设计 最佳实践是**从架构层面避免循环依赖**。可以通过以下方式: - 将公共逻辑提取到第三个Bean中,作为中介服务。 - 对原有类进行功能解耦,确保每个Bean只负责单一职责。 这不仅解决了当前的依赖问题,还能提升系统的可维护性和可测试性。 --- #### 4. 禁用Spring Boot 2.6+的循环依赖检查(慎用) 从Spring Boot 2.6开始,默认禁止循环依赖。可以通过配置启用兼容模式: ```yaml spring: main: allow-circular-references: true ``` 此方法仅建议在紧急修复生产问题时使用,因为它可能会掩盖潜在的设计缺陷。 --- #### 5. 使用 `@Primary` 或 `@Qualifier` 明确依赖优先级 在某些复杂场景中,可能存在多个候选Bean,Spring无法自动判断应该注入哪一个,也可能引发间接的循环依赖问题。通过指定主Bean或限定符可以缓解此类问题。 ```java @Primary @Service public class DefaultRoleService implements RoleService { ... } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值