SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

        今天在启动项目的时候,idea控制台突然打印了The bean 'user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.这样的错误,项目启动失败。我一脸懵逼,昨天启动的时候还好好的,怎么突然就启动不了了。百度后才发现,项目配置文件bootstrap.yml中的spring.main.allow-bean-definition-overriding=true配置不见了造成的。于是重新加上这个配置后,项目启动成功。

        但是到底发生了什么事情,为什么需要加上这个配置,它到达有什么用,不加不行吗。带着这样的疑惑,我决定从网上找找答案。

须知
在Spring Boot 2.0.x 中,spring.main.allow-bean-definition-overriding属性默认是 true;
注解@FeignClient 没有contextId属性;

在Spring Boot 2.1.x 中,spring.main.allow-bean-definition-overriding属性默认是 false;
注解@FeignClient 有contextId属性;

Spring Boot 2.1.x 已经通过增加contextId属性来避免了bean重复定义的问题,记得给contextId赋值啊。

        下面以Spring Boot 2.1.x版本来说明:

spring.main.allow-bean-definition-overriding

这个属性的意思是说如果项目中出现相同的bean命名,是否允许后面的bean覆盖前面的bean。Spring Boot 2.1.x默认配置的是false,说明当项目中出现相同的bean命名,则会报错。下面是源码说明:

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 即热spring中配置了

allowBeanDefinitionOverriding = true

 但是为什么还需要在项目的配置文件中单独配置这个属性呢。那是因为SpringBoot对这个参数进行了二次封装,并使用了boolean的默认值false:

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 现在就解释了为什么需要在配置文件单独配置allowBeanDefinitionOverriding = true。

以上是xxx.FeignClientSpecification未定义的解决办法之一。下面从源码分析为什么会出现The bean 'user.FeignClientSpecification', defined in null的问题,还有没有其他的解决办法

首先定位项目中出现此问题的地方:

在项目的feign包中,所有的xxx.feign类中的@FeignClient,value都是user。就是这个配置导致xxx.FeignClientSpecification未定义的问题。

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 查看源码:

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 在使用了@FeignClient后,spring会根据该注解去生成bean。查看源码发现此处注册了一个bean,名字就是{name}.FeignClientSpecification。这个name是作为参数传进来的。查看这个方法的调用者:

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 继续查看getClientName()方法:

SpringBoot项目启动报xxx.FeignClientSpecification问题的原因及解决办法

 通过源码发现,@FeignClient有个contextId属性,当这个属性为空时,会把value或者name属性作为bean名称。而在我的项目中,@FeignClient的value属性都指定的是user,所以bean的名字冲突了,而项目中没有配置spring.main.allow-bean-definition-overriding=true,所以会报错。这就引出了另一个解决办法,那就是在@FeignClient中指定contextId,且contextId在项目中是唯一的。

@FeignClient(value = "user", contextId = "SysCarFeign")

 现在有两种方案可以解决此问题,那究竟是配置spring.main.allow-bean-definition-overriding=true,还是在@FeignClient中指定contextId。对于这个问题,我没有进一步的查找资料,个人观点是在@FeignClient中指定contextId为好,因为这样会保证项目中所有的bean都能被Spring管理起来。而配置spring.main.allow-bean-definition-overriding=true会导致前面的bean被后面相同名字的bean覆盖,会产生很多意想不到的情况。

上一篇:深度系统监视器原理剖析


下一篇:2020-12-25