场景复现 依赖:
Spring Boot 2.1.6.RELEASE
Eureka Client 2.1.0.RELEASE
OpenFeign 2.1.0.RELEASE
我们创建两个项目, ahao-server
服务提供方和ahao-client
服务调用方.Eureka
可以使用我弄的一个开箱即用Eureka
在ahao-server
创建一个显示当前时间的controller
, 同时注册到eureka
上. 假设端口为http://localhost:8080
1 2 3 4 5 6 7 8 9 @RestController @RequestMapping("/ahao") public class TimeController { @RequestMapping("/date") public String date () { return "现在日期是:" + new SimpleDateFormat ("yyyy年MM月dd日" ).format(new Date ()); } @RequestMapping("/time") public String time () { return "现在时间是:" + new SimpleDateFormat ("hh时mm分ss秒" ).format(new Date ()); } }
在ahao-client
创建一个controller
和两个feign
客户端, 同时注册到eureka
上. 假设端口为http://localhost:8081
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RestController @RequestMapping("/ahao") public class TimeController { @Autowired private DateApi dateApi; @Autowired private TimeApi timeApi; @RequestMapping("/date") public String date () { return dateApi.date(); } @RequestMapping("/time") public String time () { return timeApi.time(); } } @FeignClient(value = "AHAO-SERVER", path = "/ahao") public interface DateApi { @RequestMapping("/date") String date () ; } @FeignClient(value = "AHAO-SERVER", path = "/ahao") public interface TimeApi { @RequestMapping("/time") String time () ; }
运行ahao-client
报错
1 2 3 4 5 6 7 8 9 10 *************************** APPLICATION FAILED TO START *************************** Description: The bean 'AHAO-SERVER.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 Process finished with exit code 1
这个叫做AHAO-SERVER.FeignClientSpecification
的Bean
是从哪来的???
问题所在 Spring
打印出了异常堆栈. 我们跟进去看一下.
1 2 3 4 5 6 7 class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware, EnvironmentAware { private void registerClientConfiguration (BeanDefinitionRegistry registry, Object name, Object configuration) { registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); } }
我们可以看到, 这里注册了一个Bean
, 名字就是AHAO-SERVER.FeignClientSpecification
. 这个name
是从外部传进来的.
1 2 3 4 5 6 7 8 9 10 11 12 13 class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware, EnvironmentAware { public void registerFeignClients (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration" )); registerFeignClient(registry, annotationMetadata, attributes); } }
可以看到, name
应该是从注解中的属性取值来的, 再看看getClientName()
方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware, EnvironmentAware { 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()); } }
一目了然了, 我们声明@FeignClient
注解时, 只使用了value
属性, 所以产生了冲突, 只要加上contextId
就好了.
解决方案 加上contextId
属性即可.
1 2 3 4 5 6 7 8 @FeignClient(value = "AHAO-SERVER", path = "/ahao", contextId = "AHAO-SERVER-DATE") public interface DateApi { @RequestMapping("/date") String date () ; } @FeignClient(value = "AHAO-SERVER", path = "/ahao", contextId = "AHAO-SERVER-TIME") public interface TimeApi { @RequestMapping("/time") String time () ; }
Spring Boot/Cloud 2.0.x 版本 我们切换到Spring Boot/Cloud 2.0.x
版本, 发现没有contextId
属性, 但是启动的时候可以正常启动, 不会报错Bean
冲突. 看下Spring Boot/Cloud 2.0.x
版本的源码.
1 2 3 4 5 6 7 8 9 10 11 12 class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware, EnvironmentAware { private String getClientName (Map<String, Object> client) { if (client == null ) { return null ; } String 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()); } }
看下getClientName()
方法, 里面也没有使用contextId
. 也就是会创建两个同名AHAO-SERVER.FeignClientSpecification
的Bean
. 后来翻了下issue
发现了答案.
spring boot 2.0.x spring.main.allow-bean-definition-overriding default value is “true” spring boot 2.1.x default value changed to “false”
原来是允许Bean
重复定义所以才没有报错. 关键在spring.main.allow-bean-definition-overriding
这个属性.
Spring Boot 2.0.x
默认是 true
.
Spring Boot 2.1.x
默认是 false
.
参考资料