【Spring Boot】Spring Boot解决循环依赖
循环依赖的定义与问题
循环依赖是指两个或多个Bean相互依赖,形成闭环。例如Bean A依赖Bean B,Bean B又依赖Bean A。Spring默认不支持这种场景,会抛出BeanCurrentlyInCreationException。
使用构造器注入的解决方案
构造器注入是Spring推荐的方式,但无法直接解决循环依赖。需要在设计层面避免这种场景,或改用setter注入。重构代码是更根本的解决方式,例如提取公共逻辑到第三个Bean中。
使用setter注入解决循环依赖
Spring通过三级缓存机制支持setter注入的循环依赖:
- 一级缓存:存放完全初始化好的Bean
- 二级缓存:存放早期暴露的原始Bean(未填充属性)
- 三级缓存:存放Bean工厂,用于生成原始对象
// Bean A
@Component
public class A {private B b;@Autowiredpublic void setB(B b) {this.b = b;}
}// Bean B
@Component
public class B {private A a;@Autowiredpublic void setA(A a) {this.a = a;}
}
使用@Lazy延迟加载
在其中一个依赖上添加@Lazy注解,延迟初始化Bean:
@Component
public class A {private final B b;public A(@Lazy B b) {this.b = b;}
}
使用ApplicationContext主动获取
在Bean初始化完成后通过ApplicationContext获取依赖:
@Component
public class A implements ApplicationContextAware {private B b;@Overridepublic void setApplicationContext(ApplicationContext ctx) {this.b = ctx.getBean(B.class);}
}
调整Bean初始化顺序
通过@DependsOn注解强制指定初始化顺序:
@Component
@DependsOn("b")
public class A {@Autowiredprivate B b;
}@Component
public class B {// 无需依赖A
}
最佳实践建议
- 优先考虑重构代码结构,消除循环依赖
- 必须使用循环依赖时选择setter注入方式
- 对于特定场景可以使用
@Lazy临时解决方案 - 避免在构造函数中进行业务逻辑操作
循环依赖可能掩盖设计问题,应谨慎使用解决方案而非将其作为常规设计模式。
