为什么@ConditionalOnMissingBean在应检查的bean之前被调用?

时间:2019-02-20 14:19:55

标签: spring spring-boot kotlin

在Spring Boot应用程序中,我希望仅在未启用模块的情况下才能创建bean。

在CoreConfig中,我定义了两个bean(地理编码器和travelDistanceCalculator) 带有@ConditionalOnMissingBean的注释,引用了这些bean应该初始化的接口。

@Configuration
class CoreConfig {

    private val logger = loggerFor<CoreConfig>()

    @Bean
    @ConditionalOnMissingBean(Geocoder::class)
    fun geocoder(): Geocoder {
        logger.warn("no Geocoder bean instance has been provided. Instantiating default.")

        return object : Geocoder {
            override fun getGeocode(address: String) = Coordinates.Unavailable
        }
    }

    @Bean
    @ConditionalOnMissingBean(TravelDistanceCalculator::class)
    fun travelDistanceCalculator(): TravelDistanceCalculator {
        logger.warn("no TravelDistanceCalculator bean instance has been provided. Instantiating default.")

        return object : TravelDistanceCalculator {
            override fun getTravelDistanceInKm(origin: Coordinates, destination: Coordinates) = Double.NaN
        }
    }

    // other beans definitions...
}

然后GeolocationConfig定义了同时实现Geocoder和TravelDistanceCalculator接口的bean(HereApiClient)。

@Configuration
@ConditionalOnProperty("app.geolocation.enable")
class GeolocationConfig {

    @Bean
    fun hereApiClient(
        geolocationProperties: GeolocationProperties,
        restTemplate: RestTemplate
    ): HereApiClient =
        HereApiClient(restTemplate, geolocationProperties)

}

app.geolocation.enable在application.yml中定义为true

这里发生的是,在启动时,即使启用了GeolocationConfig并在它们之后不久初始化了HereApiClient,也会初始化CoreConfig中定义的geocoder和travelDistanceCalculator默认bean。

我在这里想念什么?

2 个答案:

答案 0 :(得分:2)

ConditionalOnMissingBean取决于@Configuration类的处理顺序和定义其bean的顺序。在您的示例中,我怀疑正在CoreConfig Bean被处理之前,正在hereApiClient的处理,其条件的评估以及其Bean的定义。结果,当评估缺少的bean条件时,找不到匹配的bean,并且定义了geocodertravelDistanceCalculator bean。

ConditionalOnMissingBean的Javadoc提出以下建议:

  

该条件只能匹配到目前为止应用程序上下文已处理的bean定义,因此,强烈建议仅在自动配置类上使用此条件。如果候选bean可能是由另一种自动配置创建的,请确保使用此条件的豆在此之后运行。

您可以通过在配置类上使用@Order来遵循此建议。或者(我认为最好是)您可以将CoreConfig设置为自动配置类,方法是将其列在META-INF/spring.factories键下的org.springframework.boot.autoconfigure.EnableAutoConfiguration中,然后将其移动到不会通过组件扫描拾取。

答案 1 :(得分:1)

@ConditionalOn(Missing)Bean旨在用于自动配置类。 see javadoc 如果在通用配置类中使用,那么结果取决于首先加载的配置。见安迪·威尔金森答案

在您的情况下,您可以轻松地使用相同的属性(已经提供)来配置正确的bean。

@ConditionalOnProperty(name="app.geolocation.enable")上使用GeolocationConfig

@ConditionalOnProperty(name="app.geolocation.enable",havingValue="false")上/之中使用CoreConfig

编辑

如果缺少属性,则要使用@ConditionalOnProperty(name="app.geolocation.enable",havingValue="false",matchIfMissing = true),请使用CoreConfig