1 场景复现
依赖关系:虚线左侧为实现的类,虚线右侧为Spring的原生接口类或注解。
正常看,没有循环依赖,但是,实际运行时,异常信息:Is there an unresolvable circular reference?
明确告知,可能存在循环依赖,需要进一步从异常日志排查。
1.1 FeignClient
通过Feign调用服务。
1.2 HandlerInterceptor
新建Token拦截器。
1.3 WebMvcConfigurer
添加Token拦截器。
1.4 Controller
接口依赖FeignClient。
2 方案
在TokenInterceptor的FeignClient添加@Lazy,启动SpringBoot时不加载TokenInterceptor的FeignClient,当服务启动完成后,有需要时再加载,这样就不用再启动时重复验证单例FeignClient,保证服务正常启动。
3 分析
SpringBoot启动时堆栈信息如下:从内往外抛出异常,
异常信息较多,分段看。
-
第一段
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘rpcApi’: Unsatisfied dependency expressed through field ‘feignTemplateService’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration’: Unsatisfied dependency expressed through method ‘setConfigurers’ parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘webMvcHandler’: Unsatisfied dependency expressed through field ‘tokenInterceptor’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tokenInterceptor’: Unsatisfied dependency expressed through field ‘templateService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘com.monkey.tutorial.common.rpc.IFeignTemplateService’: Requested bean is currently in creation: Is there an unresolvable circular reference? -
第二段
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration’: Unsatisfied dependency expressed through method ‘setConfigurers’ parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘webMvcHandler’: Unsatisfied dependency expressed through field ‘tokenInterceptor’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tokenInterceptor’: Unsatisfied dependency expressed through field ‘templateService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘com.monkey.tutorial.common.rpc.IFeignTemplateService’: Requested bean is currently in creation: Is there an unresolvable circular reference? -
第三段
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘webMvcHandler’: Unsatisfied dependency expressed through field ‘tokenInterceptor’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tokenInterceptor’: Unsatisfied dependency expressed through field ‘templateService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘com.monkey.tutorial.common.rpc.IFeignTemplateService’: Requested bean is currently in creation: Is there an unresolvable circular reference? -
第四段
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘tokenInterceptor’: Unsatisfied dependency expressed through field ‘templateService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘com.monkey.tutorial.common.rpc.IFeignTemplateService’: Requested bean is currently in creation: Is there an unresolvable circular reference? -
第五段
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘com.monkey.tutorial.common.rpc.IFeignTemplateService’: Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355) ~[spring-beans-5.3.6.jar:5.3.6]
3.1 定位异常
从第五段异常信息可以定位到抛出异常的位置:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation,
这个方法是新建单例对象前的校验,源码如下图所示:
由源码可知,新建单例对象前都会通过beforeSingletonCreation校验需要创建的单例对象是否在当前集合中,根据源码的判定逻辑,如果需要创建的单例对象名称(beanName)在singletonsCurrentlyInCreation中,则抛出异常。
3.2 调试方案
通过异常定位以及对抛出异常的逻辑分析,
可在beforeSingletonCreation处打断点调试,下面是几个核心位置的调试信息:rpcApi、WebMvcHandler、TokenInterceptor和IFeignTemplateService。
3.2.1 rpcApi
rpcApi校验创建单例对象调试信息如下图所示,
图中右侧,this.singletonsCurrentlyInCreation集合为空,因此,可以通过校验。
3.2.2 IFeignTemplateService
进入IFeignTemplateService校验,由于rpcApi依赖IFeignTemplateService,
因此,rpcApi校验结束后,会进入IFeignTemplateService校验,
调试结果如下图所示,由图可知,IFeignTemplateService的this.singletonsCurrentlyInCreation已经存在一个元素rpcApi,
即rpcApi依赖IFeignTemplateService,而IFeignTemplateService不在集合singletonsCurrentlyInCreation中,因此校验通过。
3.2.3 mvcResourceProvider
接下来校验的脚步会被mvcResourceUrlProvider截胡,
先进行mvcResourceUrlProvider校验,
调试结果如下图所示,由图可知,this.singletonsCurrentlyInCreation中已存在rpcApi和IFeignTemplateService,
说明rpcApi和IFeignTemplateService是依赖mvcResourceUrlProvder的,此时,校验通过,
这里的核心是:IFeignTemplateService已经添加到this.singletonsCurrentlyInCreation,是后续抛出异常的引子。
3.2.4 webMvcHandler
mvcResourceUrlProvider依赖webMvcHandler,
mvcResourceUrlProvider校验通过后,会进入webMvcHandler校验,
调试结果如下图所示,由图可知,webMvcHandler校验通过,无异常。
3.2.5 tokenInterceptor
webMvcHandler依赖tokenInterceptor,校验webMvcHandler后,会进入tokenInterceptori校验,
tokenInterceptor校验过程如下图所示,校验通过。
3.2.6 IFeignTemplateService
由于tokenInterceptor也依赖IFeignTemplateService,
所以,tokenInterceptor校验通过后,会接着校验IFeignTemplateService,
调试过程如下图所示,综合上面的分析,this.singletonsCurrentlyInCreation中已经存在IFeignTemplateService,
(由于IFeignTemplateService依赖mvcResourceUrlProvider;mvcResourceUrlProvider依赖webMvcHandler)
校验失败,抛出异常。
抛出异常
3.3 循环依赖
经过分析,形成的循环依赖如下图所示。
3.4 解决
为了保证SpringBoot服务,需要打破循环依赖,
所以,在TokenInterceptor中,在服务启动时不加载FeignClieit,
在FeignClient上面添加@Lazy注解,延迟加载,服务启动后,有调用时再加载。
4 小结
(1)循环依赖有显式依赖:如A->B->C->A,以及隐式依赖,需要逐步调试;
(2)循环依赖的解决方案:断开环,可调整代码设计、延迟加载等方式。