记一次spring循环依赖问题
今天上班的时候,发版突然出现问题,使用相同的分支相同的commit,甚至是正在生产环境运行的分支,也不能发版成功。但是,我idea一直都是可以成功运行的。
查看日志报错如下:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Bean with name 'DetailServiceImpl' has been injected into other beans [ConfigServiceImpl] 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.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:649)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1389)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:544)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:520)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:673)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:329)
看到报错,一直在想,为什么明明一样的内容,突然就不行了呢?
当时就大大的疑问:
(1)spring不是自己处理了循环依赖吗,为什么还会报这种错?
(2)为什么我本地可以?
(3)为什么相同的分支相同的commit去发版,却突然不行啦?
查阅网上资料
有说是因为你的bean使用的是构造函数注入方式,有说是因为@Async注解导致的。
但是,我的代码里面都不是这两种情况。
最后翻到一篇文章spring bean 循环依赖问题,在本地环境可以,测试环境报循环依赖问题
幡然醒悟,核心思想是:
在不同的操作系统或者环境下, bean 的加载顺序是不固定的。bean 加载顺序变化之后,就可能会导致循环依赖报错问题产生!因为,顺序变化之后,循环依赖的主体变了!
bean 加载时,会先将所有的 BeanDefinition 扫描出来,扫描出来的顺序基本上就决定了 bean 的加载顺序。
扫描 BeanDefiniton 的方法是 ClassPathScanningCandidateComponentProvider#scanCandidateComponents(),这个方法在不同的环境下扫描出类的顺序是不固定的。
它的底层走的是 java.lang.ClassLoader#getResources() ,这个方法没有承诺获取到资源文件的顺序!
我本地一直可以,是因为idea有类似缓存的处理。
那么,怎么解决呢?根据报错提示是DetailServiceImpl与ConfigServiceImpl的循环依赖问题,那么就去看这两个类的依赖bean。
解决办法如下:
- 去循环依赖
- @Lazy
- InitializingBean 时,从 context 中获取
- @PostConstruct 去设置 bean 依赖
至此:念头通达。