springcloud 拦截器注入Feign接口导致循环依赖

bridge
2021-03-03 / 0 评论 / 3 点赞 / 1,794 阅读 / 7,774 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-01-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.问题背景

  • 某次需求,需要在拦截器中注入调用另一个系统的Springcloud服务接口,并且此接口通过Feign的方式调用。然而在工程启动的时候报错了.报了:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
	Error creating bean with name 'mvcResourceUrlProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?

2.代码

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
 
    @Autowired
    private AuthInterceptor authInterceptor;
 
    @Bean
    public WebMvcConfigurer WebMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                // ...省略无关代码
                registry.addInterceptor(authInterceptor);
                // ... 省略无关代码
            }
        }
    }
}
 
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private AuthInfoFeign authInfoFeign;
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler) throws Exception {
        // ... 省略无关代码
        Result<AuthInfo> result = authInfoFeign.doService();
        // ... 省略无关代码
    }
}
 
 
@FeignClient(value = "AService")
public interface AuthInfoFeign extends AuthenticationInfoApi {
 
}

3.异常报错

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 'webConfiguration': Unsatisfied dependency expressed through field 'authencationInterceptor'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authencationInterceptor': Unsatisfied dependency expressed through field 'authenticationInfoFeign'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.service.feign.crm.AuthInfoFeign': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mvcResourceUrlProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:676)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$137/1161148117.getObject(Unknown Source)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:392)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$137/1161148117.getObject(Unknown Source)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
    at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:239)
    at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:196)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:133)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
    at org.springframework.context.support.AbstractApplicationContext.registerListeners(AbstractApplicationContext.java:840)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
    at com.xxx.web.WebApplication.main(WebApplication.java:27)
  • 提示AuthenticationInfoFeign在创建bean的时候依赖注入mvcResourceUrlProvider循环依赖了: Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mvcResourceUrlProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?

4.处理思路

  1. debug报错代码,DefaultSingletonBeanRegistry中初始化单例bean之前的校验不通过,发现已经存在了mvcResourceUrlProvider,故抛出异常报错提醒。注:DefaultSingletonBeanRegistry这个类主要用作基类的BeanFactory实现, 提供基本的管理 singleton bean 实例功能
  2. 原因找到了,只要解决mvcResourceUrlProvider不被重复注入即可,怀疑是否是@FeignClient生成代理对象注入Bean容器时触发的,通过feign的原理学习和源码并未发现它会注入mvcResourceUrlProvider。
    mvcResourceUrlProvider重复注入的点并未找到, 期待其他的小伙伴指正答疑。
  3. 改变思路
    1. 循环依赖的错误那么我们就截断依赖注入。
      在拦截器注入FeignClient时,不需要spring的自动注入, 而是手动从Spring上下文中获取bean
    2. 既然拦截器中注入的AuthenticationInfoFeign ,那么我们将它延时注入,启动时不将feignClient注入,而是在拦截器被配置初始化时进行注入,这不正是@Lazy注解的功能嘛。
      @Lazy:延时加载。仅对单例Bean生效。而单例Bean是在Spring容器启动的时候加载的,添加@Lazy注解后就会延迟加载,在Spring容器启动的时候并不会加载,而是在当你第一次使用此bean的时候才会加载,但当你多次获取bean的时候不会重复加载。
    3. 按照上述思路,修改代码

5.解决方法

  1. 手动获取AuthenticationInfoFeign,阻止循环依赖
/**
* SpringContextUtil自行百度
*/
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler) throws Exception {
        // ... 省略无关代码
        // 手动从Spring上下文中获取AuthenticationInfoFeign
        AuthInfoFeign authInfoFeign = SpringContextUtil.getBean(AuthInfoFeign.class);
        Result<AuthenticationInfo> result = authInfoFeign.doService();
        // ... 省略无关代码
    }
}
  1. @Lazy 延时加载AuthenticationInfoFeign
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    @Lazy
    private AuthInfoFeign authInfoFeign;
	
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler) throws Exception {
        // ... 省略无关代码
        Result<AuthenticationInfo> result = authInfoFeign.doService();
        // ... 省略无关代码
    }
}
0

评论区