Spring Boot 3.2升级后同类型多名称Bean触发歧义异常
问题背景
将应用从Spring Boot 3.0.x升级至3.2版本后,出现多个RestTemplate Bean的歧义异常,此前运行正常。应用引入了6个客户端库,每个库通过类似以下的配置类创建不同名称的RestTemplate Bean:
@RequiredArgsConstructor @AutoConfiguration @ConfigurationPropertiesScan @ComponentScan(basePackages = {"com.partner.tracker.client"}) public class TrackerClientConfiguration { @Autowired(required = false) private RestTemplateCustomizer restTemplateCustomizer; @Bean(name = "trackerRestTemplate") @ConditionalOnMissingBean(name = "trackerRestTemplate") public RestTemplate trackerRestTemplate() { return createTrackerRestTemplate(); } public RestTemplate createTrackerRestTemplate() { RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder() .requestFactory(this::customHttpRequestFactory) .interceptors( getDefaultHeadersInterceptor(), getTraceIdInterceptor() ) .messageConverters(getMessageConverters()); if (Objects.nonNull(restTemplateCustomizer)) { restTemplateBuilder = restTemplateBuilder.additionalCustomizers(restTemplateCustomizer); } return restTemplateBuilder.build(); } }
在TrackerApiClient使用Lombok @RequiredArgsConstructor 构造注入RestTemplate时,Spring检测到6个同类型Bean,触发歧义异常。临时通过手动添加@Qualifier注解修复,但该方案破坏了单元测试,且需放弃Lombok自动构造注入改为手动编写带@Qualifier的构造器。
原因分析
该问题是Spring Boot 3.2带来的行为变化,根源在于Spring Framework版本升级(Spring Boot 3.2对应Spring Framework 6.1):
- 在Spring Boot 3.0.x(Spring Framework 6.0.x)中,当存在多个同类型命名Bean且注入点未指定
@Qualifier时,Spring会尝试根据构造器参数名称匹配Bean名称,如果找到匹配项则自动注入,不会触发歧义异常。 - Spring Framework 6.1(Spring Boot 3.2)调整了歧义Bean的判定逻辑,不再默认通过参数名匹配Bean名称,只要存在多个同类型非
@PrimaryBean且注入点无@Qualifier,直接抛出歧义异常。
合理解决方案
方案1:结合Lombok保留构造注入并指定Qualifier
无需放弃Lombok,通过Lombok的onConstructor_参数为构造器添加@Qualifier注解,同时保持字段的final修饰:
@RequiredArgsConstructor(onConstructor_ = {@Qualifier("trackerRestTemplate")}) public class TrackerApiClient { private final RestTemplate restTemplate; }
注:onConstructor_是Lombok针对Java 8+的语法,确保编译时能正确生成带注解的构造器。
方案2:使用自定义RestTemplate子类
为每个客户端库定义专属的RestTemplate子类,避免类型歧义:
- 在客户端库中创建子类:
public class TrackerRestTemplate extends RestTemplate {}
- 修改配置类的Bean创建逻辑,返回子类实例:
@Bean(name = "trackerRestTemplate") @ConditionalOnMissingBean(name = "trackerRestTemplate") public TrackerRestTemplate trackerRestTemplate() { RestTemplate template = createTrackerRestTemplate(); TrackerRestTemplate trackerTemplate = new TrackerRestTemplate(); // 复制原RestTemplate的配置(如拦截器、消息转换器等) trackerTemplate.setInterceptors(template.getInterceptors()); trackerTemplate.setMessageConverters(template.getMessageConverters()); trackerTemplate.setRequestFactory(template.getRequestFactory()); return trackerTemplate; }
- ApiClient直接注入子类类型,无需
@Qualifier:
@RequiredArgsConstructor public class TrackerApiClient { private final TrackerRestTemplate restTemplate; }
该方案完全避免了类型歧义,保持Lombok构造注入的便利性,对单元测试影响最小。
方案3:启用编译参数保留参数名
确保项目编译时启用-parameters选项(Spring Boot Maven/Gradle插件默认已启用),让Spring能通过参数名匹配Bean名称:
- Maven:无需额外配置,Spring Boot插件默认添加该参数
- Gradle:确保
java插件配置中包含options.parameters = true
此时ApiClient的构造器参数名需与目标Bean名称一致,例如:
@RequiredArgsConstructor public class TrackerApiClient { private final RestTemplate trackerRestTemplate; }
Spring会自动匹配名称为trackerRestTemplate的Bean,无需添加@Qualifier。
内容的提问来源于stack exchange,提问作者VelNaga

