EnableFeignClients重复加载导致SpringBoot启动异常
2025-01-22 08:19:30    1.1k 字   
This post is also available in English and alternative languages.

@EnableFeignClients 注解配置导致重复加载问题;

The bean ‘xxx.FeignClientSpecification’, defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.


1. 项目背景

可以将SDK看做是 apache commons 工具jar包,AVA项目加载SDK,获得处理特定业务的能力。

1.1. SDK(寄生项目)

1
2
3
4
5
SpringBoot搭建
对外暴露若干Controller接口
通过 Feign 调用第三方服务

通过 `spring.factories` 扩展机制,让宿主项目加载,提供特定业务功能。

1.2. AVA项目(宿主项目)

1
2
3
4
标准SpringBoot项目
对外暴露若干Controller接口

通过加载SDK(寄生项目),来获取特定业务处理能力。

2. 项目环境描述

这两个项目不是新项目,在两个月前,就通过联调测试,并且发布到了预生产环境。此后SDK并没有再修改上传代码。

最近增加了新的需求后,开发人员将AVA项目重新构建依赖后,进行本地开发,发现AVA项目无法启动。


3. 异常描述

3.1. 异常表现

1
SpringBoot 项目启动失败

3.2. 异常信息

1
2
3
4
5
6
7
Description:
The bean 'crm-service.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

无法注册在null中定义的bean"crm.service.FeignClientSpecification"。已在null中定义了具有该名称的bean,并且已禁用重写。
考虑重命名bean中的一个,或者通过设置Spring。

4. 异常分析

虽然第一反应知道肯定不是SDK的问题(因为两个月没有提交代码了),本着负责任的态度,还是先从自身项目查起。


5. 排查

看到异常的第一反应是 @FeignClient 的名称重复了?

网上查了下,大部分的定位思路也是:FeignClient注解使用的服务名重复

解决方案是以下三种:

  1. 排查重复的@FeignClient 名称,同一个服务的接口,不要分散的写在多个接口类中

  2. @FeignClient中添加contextId属性,通过添加命名空间属性解决。

  3. 开启 spring.main.allow-bean-definition-overriding: true 配置。

SpringCloud 2.1.0 以上版本,不再默认支持 FeignClient 的name属性 的相同名字。
即多个接口上的@FeignClient(“service-name”)会报错,overriding is disabled(覆盖是禁止的)。

该配置允许 FeignClient 的名称重复.

排查以后SDK中并没有找到重复的 Feign,AVA项目也没有使用 Feign 功能。

有些郁闷,没有重复的名称,为啥会有这个异常呢?于是重新看了下异常信息,目光落在"xxx.service.FeignClientSpecification"上。

FeignClientSpecification 这个类是 spring openfeign 中的源码类,为什么会说它无法注册呢?

突然想到,SDK项目是以jar形式,提供给AVA项目进行加载的,会不会是我的jar包被重复扫描了?

找到AVA开发人员,要来代码阅读权限,拉取代码,构建相关依赖,然后开始扒它依赖的jar包。

排除掉那些 spring、apache 等的jar包,范围缩小到该组开发人员自己封装的工具jar包。

打开以后,一个个包看过去,好家伙,终于找到问题所在。

  • SDK中的自动配置类

    1
    2
    3
    4
    5
    6
    7
    @Configuration
    @ConditionalOnClass(BotSdkController.class)
    @ComponentScan("com.paxxx.crm.client")
    @EnableFeignClients(basePackages = {"com.paxxx.crm.client"})
    @MapperScan("com.paxxx.crm.client.repository")
    public class CrmClientAutoConfigure {
    }

    @EnableFeignClients注解会扫描指定路径下,所有使用注解@FeignClient定义的feign客户端。

    SDK中指定扫描的包路径是:“com.paxxx.crm.client”


  • AVA项目,该组开发人员自己封装的工具jar包

    xxx-cloud-client.jar,看下源码

    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    @EnableFeignClients(
    basePackages = {"com.&&&", "com.paxxx"}
    )
    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @EnableConfigurationProperties({XXXFeignClientProperties.class})
    public class XXXFeignAutoConfigure {

    工具包指定扫描包路径:“com.paxxx”

    @EnableFeignClients 扫描的包路径重复了。


6. 小结

自此,问题根源找到了,多个@EnableFeignClients注解,扫描同一包路径(包路径覆盖),导致 feign客户端 被重复加载。


7. 思考

从项目、工程、应用角度看,SDK中的代码是没有问题的。

虽然最后还是我被迫做了调整。

反观AVA项目中自己封装的工具包,将@EnableFeignClients放在工具包中,感觉实在不妥。

即便是放置在工具包中,也应该提供开关、配置,对重复、覆盖的包路径进行妥善处理。