故事
在开发中,本地代码稳定运行,测试环境也没问题,而生产环境发生了循环依赖,发布失败了。糟糕,生产发布失败可是大事情。开始着手解决。
现象
看看错误先
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘customizeThreadServiceImpl’: Bean with name ‘customizeThreadServiceImpl’ has been injected into other beans [testStepServiceImpl,agileTestDetailServiceHelper,agileDemandServiceHelper,agileTaskOperationLogic,agileDemandRelationServiceHelper,agileTestPlanServiceHelper,agileStoryOperationLogic,issueViewServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using ‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.
分析
看上面的报错,很明显原因是customizeThreadServiceImpl注入其他类的时候,发生了循环依赖,注入失败了
解决办法
- 使用@Lazy的注解,注解到发生循环依赖的类引入上(最简单)
@Lazy
@Resource
private CustomizeThreadService threadService;
- 去除循环依赖,改造代码(困难但必要)
因为发生循环依赖的根源还是代码设计不合理,产生的循环依赖,要从根源上解决,就是从设计程序的基础上就应该规划好,及早预防 - InitializingBean时,从context中获取(不推荐)
- @PostConstruct去设置bean依赖(不推荐)
到这里还是没搞明白为什么本地不会循环依赖,测试环境也不会,为什么生产就会呢
真正的原因
在不同的操作系统或者环境下,bean的加载顺序是不固定的。bean加载顺序变化之后,就可能导致循环依赖的产生。因为顺序变化之后,循环依赖的主体发生变化。
bean加载时,会先将所有的BeanDefinition扫描出来,扫描出来的顺序基本上决定了bean的加载顺序。
扫描BeanDefinition的方法是ClassPathScanningCandidateComponentProvider#scanCandidateComponents(),这个方法在不同的环境下扫描出类的顺序是不固定的,它底层走的是java.lang.ClassLoader#getResources,这个方法没有承诺获取到资源文件的顺序。
总结
遇到这种循环依赖,不要心存侥幸,还是要着手解决为妙
遇事总结,分享工作趣事,祝我们都工作顺利。