spring@import正在忽略在同一类上定义的@conditionalonbean

np8igboo  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(338)

我正在尝试设置一个自定义Spring自动配置。
自定义自动配置需要导入常规的spring配置,但仅当上下文中存在bean时。
到目前为止,一个简单的 @ConditionalOnBean 在要导入的配置上,应按照文档中的明确说明执行:
如果@configuration类被标记为@conditional,那么与该类关联的所有@bean方法、@import注解和@componentscan注解都将受这些条件的约束。
我不想设定一个 @ConditionalOnBean 在常规Spring配置上(不在自动配置模块中)
所以我创建了第二个配置类,它只用于有条件地导入我的“旧常规配置”
这是相关代码。
自定义自动配置类

package myproject.lib.autoconfigure

@Configuration
@AutoConfigureAfter(RabbitAutoConfiguration.class)
@Import(ImportSpecificConfig.class)
public class ObjectStorageFacadeAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(HttpClient.class)
    public HttpClient httpClient() {
        return HttpClient.newHttpClient();
    }

    //a few other beans...
}

导入程序配置:

package myproject.lib.autoconfigure

@ConditionalOnBean(ConnectionFactory.class)
@Configuration
@Import(SpecificConfig.class)
public class ImportSpecificConfig {
   @Bean
   public SomeBean aBean(){
     return new SomeBean();
   }
}

旧的常规配置(也会导入一堆东西)

package other.lib

@Configuration
@Import(CommonConfiguration.class)
@EnableConfigurationProperties(ObjectStorageRabbitQueueProperties.class)
@ComponentScan
public class SpecificConfig {

}

我的预期行为是不处理 @Import(SpecificConfig.class)ImportSpecificConfig.class 当没有 ConnexionFactory  豆子出现了。它也不应该在上下文中示例化 SomeBean 豆子。
然后我写了一个测试:

class ObjectStorageFacadeAutoConfigurationTest {
    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(ObjectStorageFacadeAutoConfiguration.class));
    @Test
    void autoConfiguredCacheManagerIsInstrumented() {
        this.contextRunner
                .run((context) -> {
                    assertThat(context).hasSingleBean(HttpClient.class);
                });
    }
}

此测试在启动上下文时失败。认为我在上下文中缺少了一些bean,这些bean是示例化在中定义的bean所必需的。。。 SpecificConfig 包裹。
所以我的理解(我可以用debuger确认)是 @Import 中的注解 ImportSpecificConfig 即使上下文没有任何与 @ConditionalOnBean 条件。这触发了 @ComponentScan 以及 @Import 旧的常规配置中的注解,由于上下文中不存在所需的bean(这是预期的),因此测试失败。
顺便说一下 SomeBean 实际上没有像预期的那样示例化。
奇怪的是如果我用 @ConditionalOnBean 在老规矩上 SpecificConfig 在配置类中,将发生预期的行为: @Import(CommonConfiguration.class) 未激活,则上下文初始化没有任何问题。
当然,这不是一种正确的方法,因为我不想修改lib源代码。
热释光;博士;这个 @Import 在一个 @Configuration 对带注解的类进行求值,即使 @Configuration 班级有一个 @ConditionalOnBean 计算结果为假。以及这个定义中的bean @Configuration 类没有示例化,这证实了 @ConditionalOnBean 计算结果为false。似乎@componentscan才是真正的问题。我认为这不应该被评估,但它是。
这是一个简单的可复制的例子https://github.com/fdeguibert/sample
我不明白什么?这有什么诀窍吗 @ConditionalOnBean 我看不到或者这是一种不好的行为?

wkftcu5l

wkftcu5l1#

经过更多的挖掘和感谢@andywilkinson,我意识到了我的错误。
@conditionalonbean是一个有效的注册bean阶段条件。所以@import忽略了它,显然@componentscan也忽略了它,因为在parse\u配置阶段需要这个注解。
因此,如果我的理解是正确的,那么在@configuration类型级别应用的@conditionalonbean实际上将由@configuration类中定义的bean使用,而不是由类本身使用。
检查它的一个好方法是用 @Conditional(FalseCondition.class) 试图定义 FalseCondition 有两种方式:
第一:

public class FalseCondition implements ConfigurationCondition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }

    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }
}

->生成与@conditionalonbean相同的结果:计算@import
第二:

public class FalseCondition implements ConfigurationCondition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }

    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.PARSE_CONFIGURATION;
    }
}

->@import未计算。因为这个条件在解析配置阶段适用。

相关问题