@Primary标注的Bean未生效,是否必须为默认Bean添加@ConditionalOnMissingBean?
结论
这个行为完全符合Spring的设计逻辑,你没有遗漏基础配置,核心问题出在同名称Bean的注册规则和@Primary的生效前提上。
根因分析
- @Primary的生效前提是容器中同时存在多个同类型的Bean,此时在没有指定
@Qualifier的注入场景下,Spring会优先选择标记了@Primary的Bean。 - 你的两个配置类中定义的Bean方法名完全相同,Spring默认使用方法名作为Bean名称,所以两个配置类生成的是同名称、同类型的Bean定义。
- Spring Boot 2.1+版本默认
spring.main.allow-bean-definition-overriding为false,同名称Bean不会覆盖,只会保留先被扫描到的那个;如果你的版本允许Bean覆盖,则会保留后被扫描到的那个。由于checkout-library-impl的Jar包默认在classpath中的排序早于checkout-library-test,所以最终容器中只会存在默认的非@Primary的Bean,@Primary自然不会生效。 - 你给默认Bean加上
@ConditionalOnMissingBean后,默认Bean注册前会先检查是否已有同类型Bean存在,如果Test配置类的Bean已完成注册,默认Bean就会跳过注册,此时容器中只有Test的@Primary Bean,所以可以正常使用。
无需修改默认配置的解决方案
你不想加@ConditionalOnMissingBean也不想做starter,可以用以下两种方案实现需求:
- 修改Test配置类的Bean方法名(最推荐)
把Test配置类里的Bean方法名改得和默认配置不一样,避免同名称Bean冲突,这样两个同类型的Bean都会被正常注册到容器中,@Primary注解会自动生效,优先选择Test的Bean:
package com.name.core.checkout.config; @Configuration @PropertySource("classpath:checkout-test.properties") public class TestCheckoutConfiguration { @Bean @Primary CheckoutClient testCheckoutClient(final CheckoutClientConfig config) { // ... } @Bean @Primary public CheckoutManager testCheckoutManager(final CheckoutClient checkoutClient) { return new TestCheckoutManagerImpl(checkoutClient); } }
- 配置Bean覆盖规则(可选,不推荐)
在application.properties中添加配置开启Bean覆盖,同时调整checkout-library-test的Jar包依赖顺序在checkout-library-impl之前,保证Test的Bean后注册覆盖默认Bean:
spring.main.allow-bean-definition-overriding=true
该方案的缺点是依赖Jar包顺序,可移植性差,且开启全局Bean覆盖可能带来其他不可预期的问题。
内容的提问来源于stack exchange,提问作者filpa
相关产品推荐
相关产品推荐

