错误概述
在7.10号一个阳光明媚的清晨,G先生点了一杯咖啡心情愉悦的来到工位,正准备发挥新的一天的牛马精神。
同往常一样,开始pull代码 -> Clean Maven -> Install Maven,因为项目中人员较多,大家都统一前一天下班前进行 code merge,所以G先生也养成了第二天检查code merge冲突的习惯。
果然,不想看到的事情还是发生了....
Description:
The dependencies of some of the beans in the application context form a cycle:
classInfoController
↓
classInfoServiceImpl
┌─────┐
| departmentQuery
└─────┘
Action:
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.
附图:
原因
-
排查冲突Bean,
-
从上图中可以清晰看到,我们主要发生冲突的Bean在departmentQuery中
-
果然就在这个Bean中发现了奇怪的Bean注入
-
源码
/**
* 通过class获取Bean
*
* @param <T> Bean类型
* @param clazz Bean类
* @return Bean对象
*/
public static <T> T getBean(Class<T> clazz) {
return getBeanFactory().getBean(clazz);
}
这里看到 getBeanFactory().getBean()就真相大白了吧。
通俗一点说:其实就是人家spring在初始化的时候已经将所需要的Bean加载到内存中去了,
而你在其他Bean中又手动去找人家要这个对象,这个时候spring发现已经给你初始化了,你为什么还要给我要呢?
所以spring就给你抛出了BeanCurrentlyInCreationException
这里感兴趣的可以去了解一下spring的三级缓存是如何解决循环依赖
场景分析
参考文献:https://domain-driven-design.org/
-
这里我们是用@component已经将Bean托付给了Spring
-
而B先生又采用了反射的方式,去实例化Bean
@PostConstruct
public void init() {
majorService = SpringUtil.getBean(MajorService.class);
}
-
我们假设A先生 已在C类文件中通过@Autowired | @Resource 注入过此Bean
-
这时候B先生又在C类文件中调用了自己写的这个重写方法,又将此Bean注入了一遍
-
到这里 大家是不是感觉有些别扭了,没错,这就是导致错误的根本原因!
-
最根本的原因就是 两个类相互引用对方,导致Spring在初始化bean的时候不知道先初始化哪 个,从而形成循环依赖注入。
注①:从SpringBoot 2.6开始默认禁用了循环依赖
这里不做过多赘述,对此不理解的自行百度一下Spring的三级缓存,了解一下Bean的初始化过程
参考文献:Spring中的循环依赖问题以及如何解决的笔记总结_the dependencies of some of the beans in the appli-CSDN博客
注②:如果这里有人思考 Service实现层为什么不用@service;恰好你是一名三年以上IT开发人员,建议去了解一下 领域驱动设计 相信你对架构设计会有新的见解
解决方案
-
方案一:
报错信息已经足够明显,从根本上解决这个问题,不要让它们相互依赖,重新设计代码结构,代码解耦肯定是最优解!如果项目组中有 code review ,这一方案必须优先采取。
-
方案二
添加@Lazy注解,如果有参与秒杀、支付相关逻辑,这个注解慎用!
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AService {
@Lazy
@Autowired
private BService bService;
}
-
方案三
在application.properties配置当中加入
spring.main.allow-circular-references: true
注:此开关主要处理json序列化与反序列化时,循环引用问题,不建议打开,很容易吞异常,在部署的时候容易发生不可知异常。
-
建议:如果本地调试,不影响团队的情况下,三种方案皆可采用;但是在 pull code 前建议采用第一种方案,处理完依赖后在进行Submit
终
追风筝的人
我成为今天的我,是在1975年某个阴云密布的寒冷冬日,那年我12岁。
我清楚的记得当时趴在一堵坍塌的泥墙后面,窥视着那条小巷,旁边是结冰的小溪。
许多年过去了,人们说陈年旧事可以被埋葬,然而我终于明白这是错的,因为往事会自行爬上来。
回首前尘,我意识到在过去二十六年里,自己始终在窥视着那荒芜的小径。