简单来说就是在你的某个 AService 中(已经注入到容器),注入了 BService ,但是在BService 中, 也同时注入了 AService,两个 service 形成了循环互相依赖。
在一般的场景中更多的是说
A 依赖 B, B 依赖 C, 然而 C 反过来依赖 A 这样也构成了循环
当循环形成会导致无法判断先注入那一个导致系统抛出错误
The dependencies of some of the beans in the application context form a cycle
即 某些类在系统上下文中形成了一个循环
当使用spring boot 时且版本升级到较高时系统启动时若检查到循环依赖会直接错误导致无法启动。
2.3 系列版本不会检查出改问题,但是若升级到2.6及以上 系列则会启动失败
一个方法是修改spring boot 的启动配置
spring:
main:
allow-circular-references: true
此配置为允许循环依赖的存在,但不是很推荐
一般来说形成循环依赖都是因为代码结构设计不合理,导致互相依赖,实际上应将服务方法等单独提出作为公共类或底层服务取消循环的形成,但也有少部分情况下一定需要这样的写法,那么下面也有其他方式可以解决。
实际代码设计时如果代价或者时间允许情况下可以考虑代码结构的再设计或者通过 applicationContext 来提取公共类,作为单独属性来使用服务从而解决循环依赖。
即类加载属性时 我们不对要使用的类直接使用 @Autowired 注入,而是当作一般的类注入,通过set 方式来完成需要时再注入,也就是延迟了注入的时机从而解决同时加载时的循环依赖错误
@Service
public class XxxxxxService {
private XxxxAService xxxxAservice;
@Autowired
public void setCatalogRepository(XxxxAService xxxxAservice) {
this.xxxxAservice = xxxxAservice;
}
}
如果上面那样还是没有解决那么可以加个 @Lazy 如下(自己摸索的方法 可能不适用)
@Service
public class XxxxxxService {
private XxxxAService xxxxAservice;
@Autowired
public void setCatalogRepository(@Lazy XxxxAService xxxxAservice) {
this.xxxxAservice = xxxxAservice;
}
}
实际上 @Lazy 注解 与上面使用set 方法解决的原理基本一致,使用 @Lazy 后属性在启动时将不会立即去注入,而是实际使用时再去 注入该属性。
实际使用可以如下
@Service
public class XxService {
private CcAService ccAservice;
@Autowired
public XxService(@Lazy CcAService ccAservice) {
this.ccAservice = ccAservice;
}
}
或者 直接在被循环注入的类上再加注解 @Lazy (所在包 import org.springframework.context.annotation.Lazy;)
具体可以参考之前遇到的问题链接看是否是一样的错误
Bean with name ‘XX‘ has been injected into other beans [XX,XX] in its raw version… 错误分析及解决
写法比较麻烦,大致示例如下:
原理与上面一致 基本也是在错开两个类的注入顺序,从而完成解锁
@Service
public class XxService {
@Autowired
private CcAService ccAservice;
@PostConstruct
public void init() {
ccAservice.setXxService(this);
}
public CcAService getCcAService() {
return ccAservice;
}
}
在另一个类中
@Service
public class CcAService {
@Autowired
private XxService xxService ;
public void setXxService(XxService xxService) {
this.xxService = xxService;
}
}
检查代码需要的service一类 是否可以通过其他类同等替代,替代后也可以达成解锁的效果