导入含@ConditionalOnMissingBean的@AutoConfiguration类的Bean实例化问题
你遇到的核心问题是@Import的处理顺序:Spring会优先处理@Import导入的配置类(ConfigB),再处理当前配置类(ConfigA)。当ConfigB被处理时,ConfigA中的foo1还未注册,因此@ConditionalOnMissingBean(name = "foo1")条件成立,foo2被实例化;后续处理ConfigA时又创建了foo1,最终导致两个Foo类型Bean共存。
以下是几种保留@Import(ConfigB.class)且仅实例化foo1的解决方案:
方案1:调整自动配置类的执行顺序
由于ConfigB是@AutoConfiguration标注的自动配置类,可通过@AutoConfigureAfter强制让它在ConfigA之后执行:
@AutoConfiguration @AutoConfigureAfter(ConfigA.class) public class ConfigB { @Bean @ConditionalOnMissingBean(name = "foo1") Foo foo2(){} // 你需要引入的其他Bean定义 }
这样Spring会先完成ConfigA的处理,创建foo1;处理ConfigB时,@ConditionalOnMissingBean条件不满足,foo2不会被实例化,同时ConfigB中的其他Bean能正常加载。
方案2:扩展条件判断的搜索范围
修改@ConditionalOnMissingBean的搜索策略,让它检查整个上下文的Bean定义(包括尚未实例化但已解析的Bean):
@AutoConfiguration public class ConfigB { @Bean @ConditionalOnMissingBean(name = "foo1", search = SearchStrategy.ALL) Foo foo2(){} // 你需要引入的其他Bean定义 }
SearchStrategy.ALL会让Spring感知到ConfigA中已定义的foo1,即使ConfigB先被处理,也会跳过foo2的创建。
方案3:拆分ConfigB的Bean定义
如果允许修改ConfigB的结构,将foo2和其他业务Bean拆分为独立配置类:
// 仅包含需要引入的其他Bean @AutoConfiguration public class ConfigBOtherBeans { // 其他Bean的定义 } // 单独包含foo2的配置类 @AutoConfiguration public class ConfigBFoo { @Bean @ConditionalOnMissingBean(name = "foo1") Foo foo2(){} }
然后在ConfigA中仅导入包含目标Bean的配置类:
@Configuration @Import({ConfigBOtherBeans.class}) public class ConfigA { @Bean Foo foo1(){} }
这种方式彻底避免了foo2的加载,同时保留了你需要的其他Bean,也符合单一职责原则。
内容的提问来源于stack exchange,提问作者Duje Drobac

