记一次两个FeignClient接口使用相同服务名报错问题

场景

我在一个项目开发中需要远程调用其他微服务的接口进行业务处理,由于涉及到多个接口的调用,所以我根据业务分了两个FeignClient接口,但是使用的服务名是同一个,代码如下

用户相关的服务

@FeignClient(value = "base-data")
public interface UserClient{
    // 省略无关代码....
}

推送相关的服务

@FeignClient(value = "base-data")
public interface MessageClient{
    // 省略无关代码....
}

分好之后就需要进行开发调试,在启动项目的过程中出现异常导致项目无法正常启动,异常信息如下

Description:

The bean 'base-data.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

Disconnected from the target VM, address: '127.0.0.1:53658', transport: 'socket'

Process finished with exit code 1

异常信息提示是由于名称为 base-data.FeignClientSpecification 在Spring环境中已经注册了,无法再次进行注册

在此异常信息上面还有一条异常信息

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'base-data.FeignClientSpecification' defined in null: Cannot register bean definition

根据抛出的异常可以判断是处理BeanDefinition是发生的异常

解决

直接根据异常名称 BeanDefinitionOverrideException 进行搜索定位具体的异常类,使用idea查看该类在什么地方被使用到,于是发现只有一处代码使用了该类,就是在DefaultListableBeanFactory#registerBeanDefinition()

通过源码可以看出来是在进行BeanDefinition注册的时候发现该名称的BeanDefinition已经存在,然后又去判断是否允许BeanDefinition被覆盖,Spring在启动时默认使用false,也就是不允许覆盖,于是就抛出异常

但是上面两个FeignClient除了服务名相同其他都不相同,于是通过调试来查看最终注册的beanName是什么

在异常地方打个断点通过控制台查看方法调用栈

通过调用栈可以看到该方法是在FeignClientRegistrar#registerBeanDefinitions方法中调用的,先从registerBeanDefinitions中一步步调试,发现有两处地方调用了DefaultListableBeanFactory的registerBeanDefinition方法,

在经过调试发现异常是在第一处注册BeanDefinition抛出的,于是进入registerClientConfiguration方法中查看

可以看到在调用registry.registerBeanDefinition()时使用的名称是当前方法传入的,在上一张图中可以看到name是通过getClientName()获取的,于是进入该方法中

该方法的入参client其实就是@FeignClient注解中的参数,方法先判断contextId是否为空,如果不为空就用contextId作为name,否则就使用value作为bean名称, value值也就是base-data,所以两个类使用相同的服务名但是不指定contextId,就会出现BeanDefinitionOverrideException 异常

改成下面这种就可以解决问题

@FeignClient(value = "base-data", contextId = "userClient")
public interface UserClient{
    // 省略无关代码....
}
             
@FeignClient(value = "base-data"。, contextId = "messageClient")
public interface MessageClient{
    // 省略无关代码....
}

你可能感兴趣的:(记一次两个FeignClient接口使用相同服务名报错问题)