使用ApplicationContextRunner时自动配置未执行的问题排查
自定义Spring Boot自动配置类Bean加载失败问题排查与解决
问题场景
测试自定义CompanyHttpClientAutoConfiguration自动配置类,期望通过该类扫描指定包加载Registrar,进而注册带有@CompanyHttpClient注解的Bean,但测试时发现SomeHttpClient实例无法被Spring上下文识别,抛出NoSuchBeanDefinitionException。
测试代码
public class CompanyHttpClientAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(CodecsAutoConfiguration.class, ClientHttpConnectorAutoConfiguration.class, WebClientAutoConfiguration.class, CompanyHttpClientAutoConfiguration.class)); @Test public void httpClientInjected(){ //ARRANGE //ACT //ASSERT this.contextRunner.withPropertyValues("org.company.http.clients.someHttpClient.baseUrl=http://localhost:8080").run(context -> assertNotNull(context.getBean(SomeHttpClient.class))); } @CompanyHttpClient(name = "someHttpClient") interface SomeHttpClient{ @GetExchange String get(); } }
自动配置类代码
@Configuration @AutoConfigureAfter(WebClientAutoConfiguration.class) @ComponentScan("org.company.http") @ConditionalOnBean(WebClient.Builder.class) public class CompanyHttpClientAutoConfiguration { }
错误日志
19:10:05.452 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext -- Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6bb75258 19:10:05.459 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory -- Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' ...(省略中间冗余日志) 19:10:05.669 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext -- Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6bb75258, started on Sun May 28 19:10:05 EDT 2023 No qualifying bean of type 'org.company.http.autoconfig.companyHttpClientAutoConfigurationTest$SomeHttpClient' available org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.company.http.autoconfig.companyHttpClientAutoConfigurationTest$SomeHttpClient' available
问题原因与解决方法
核心原因
- 自动配置类的
@ComponentScan("org.company.http")仅扫描指定包下的组件,但测试用的SomeHttpClient是测试类的内部类,不在该扫描范围内,负责处理@CompanyHttpClient注解的Registrar无法发现这个接口。 - 虽然日志显示自动配置类已被加载,但Registrar只能处理扫描范围内的注解类,无法感知测试类内部的接口。
具体解决步骤
方案一:将测试接口移至扫描包下
把SomeHttpClient接口从测试类内部移出,放到org.company.http包或其子包下,让@ComponentScan能扫描到该接口,Registrar会自动处理注解并生成Bean实例。
方案二:测试中手动注册接口实例
若不想移动接口,可在测试中通过ApplicationContextRunner手动注册Bean:
@Test public void httpClientInjected(){ this.contextRunner.withPropertyValues("org.company.http.clients.someHttpClient.baseUrl=http://localhost:8080") // 替换为实际创建SomeHttpClient实例的逻辑,比如用你的Registrar或工厂类 .withBean(SomeHttpClient.class, () -> YourHttpClientFactory.createProxy(SomeHttpClient.class)) .run(context -> assertNotNull(context.getBean(SomeHttpClient.class))); }
方案三:扩展测试上下文的扫描范围
在测试中添加自定义配置类,扩展组件扫描范围到测试类所在包:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(CodecsAutoConfiguration.class, ClientHttpConnectorAutoConfiguration.class, WebClientAutoConfiguration.class, CompanyHttpClientAutoConfiguration.class)) .withUserConfiguration(TestScanConfig.class); @Configuration @ComponentScan(basePackageClasses = CompanyHttpClientAutoConfigurationTest.class) static class TestScanConfig {}
额外验证点
- 从日志可知
webClientAutoConfiguration已加载,WebClient.BuilderBean存在,自动配置类的@ConditionalOnBean条件已满足,无需担心条件不触发问题。 - 确认处理
@CompanyHttpClient的Registrar类在org.company.http包下,确保@ComponentScan能扫描到Registrar本身,否则注解处理逻辑根本不会生效。
内容的提问来源于stack exchange,提问作者Juan Bustamante
相关产品推荐
相关产品推荐

