天天看点

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disabled

一、错误描述

    最近使用 SpringBoot  2.2.11.RELEASE 配合 Feign 开发是出现如下错误:

org.springframework.beans.factory.support.BeanDefinitionOverrideException: 
Invalid bean definition with name 'XXX.FeignClientSpecification' defined in null: 
Cannot register bean definition [Generic bean: class [org.springframework.cloud.openfeign.FeignClientSpecification];  bound.

Description:

The bean 'XXX.FeignClientSpecification' could not be registered. 
A bean with that name has already been defined and overriding is disabled.
           

二、问题原因

根据错误描述是 The bean 'XXX.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled. 在项目中定义了两个 Feign 客户端 如下所示:

@FeignClient(value = "XXXX")
public interface ApiService1 {
    @GetMapping("/user/{username}")
    UserDto query(@PathVariable("username") String username);
}
           
@FeignClient(value = "XXX")
public interface MenuApiService {

    @GetMapping("/allMenus")
    List<MenuDto> queryAll();

}
           

因为 两个 Feign 客户端中配置的 value 值是一样的 导致在启动时报如上错误。

三、解决方法

在 注解 @FeignClient 中添加  contextId  属性,并且赋予不同的值。即可解决。

四、分析

我们看在 启动类上添加的  @EnableFeignClients 注解 其中引入了  FeignClientsRegistrar 类如下所示:

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disabled

那么我们接下来在分析一下 FeignClientsRegistrar 类的源码

 1、继承层次

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disabled

我们看到此类实现了 ImportBeanDefinitionRegistrar 接口,此接口的作用就是动态注册 Bean。接口信息如下所示:

public interface ImportBeanDefinitionRegistrar {
	
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}


	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}
           

2、FeignClientsRegistrar 类 中的实现

 在 FeignClientsRegistrar 类实现了 ImportBeanDefinitionRegistrar 接口的  registerBeanDefinitions 方法、如下所示:

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disabled

接下来重点看下 registerFeignClients 方法的实现,此方法 逻辑很简单,主要就是 @EnableFeignClients 注解中设置的 clients 属性和  basePackages 属性扫描项目中所有配置了 @FeignClient  注解的类或接口,方法解析具体可 FeignClient 客户端。看下这个方法中的主要代码(其他的可自行查看源码)如下所示:

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disabled

主要代码就是:

1、调用  getClientName(attributes) 方法获取主要名称,源码如下所示:

private String getClientName(Map<String, Object> client) {
		if (client == null) {
			return null;
		}
		String value = (String) client.get("contextId");
		if (!StringUtils.hasText(value)) {
			value = (String) client.get("value");
		}
		if (!StringUtils.hasText(value)) {
			value = (String) client.get("name");
		}
		if (!StringUtils.hasText(value)) {
			value = (String) client.get("serviceId");
		}
		if (StringUtils.hasText(value)) {
			return value;
		}

		throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
				+ FeignClient.class.getSimpleName());
	}
           

这个方法的代码逻辑很简单,现获取 contenxtId,如果不存在在获取 value, value 不存在 获取name 依次获取。

 2、调用  registerClientConfiguration 方法注册 Bean, 源码如下所示:

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);

       // 关键点:根据 getClientName 方法获取的值 注册 Bean 。
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}
           

getClientName()

源码中,可以看到 name 就是 @FeignClient 注解中的 contenxtId,name,value, serviceId (已标注过期)。beanName:

name + "." + FeignClientSpecification.class.getSimpleName()

故而我们在使用相同名称的FeigClient注解时,注入到Ioc的是相同Bean名。所以错误是由FeignClientSpecification类引起的。

继续阅读