【问题标题】:Why @ConditionalOnMissingBean is invoked before the bean it should check?为什么在它应该检查的 bean 之前调用 @ConditionalOnMissingBean?
【发布时间】:2019-07-14 06:30:24
【问题描述】:

在我的 Spring Boot 应用程序中,我希望仅在未启用模块时创建 bean。

在 CoreConfig 中,我定义了两个 bean(geocoder 和 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

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

我在这里错过了什么?

【问题讨论】:

  • 两个配置都在同一个jar里吗?
  • 是的,在同一个罐子里
  • 好的,也许你可以在CoreConfig上使用@ConditionalOnProperty(name="app.geolocation.enable",havingValue="false")

标签: spring spring-boot kotlin


【解决方案1】:

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

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

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

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

编辑

如果您希望在缺少属性时使用CoreConfig,请使用@ConditionalOnProperty(name="app.geolocation.enable",havingValue="false",matchIfMissing = true)

【讨论】:

  • 我在想核心模块不应该对地理定位模块一无所知,这就是为什么我没有提到“app.geolocation.enable”,而是因为属性和配置在同一个模块中,我只是想多了。
  • @noiaverbale 如果你真的想要核心模块不应该对地理定位模块一无所知,你应该采用安迪的方法。另一方面,您“需要”一个属性来配置GeolocationConfig,所以......一个小的改进可能是CoreConfig 在没有该属性的情况下使用。见编辑
【解决方案2】:

ConditionalOnMissingBean 取决于处理@Configuration 类和定义它们的bean 的顺序。在您的示例中,我怀疑 CoreConfig 正在被处理,它的条件被评估,并且它的 bean 在你的 hereApiClient bean 被定义之前被定义。因此,在评估缺少的 bean 条件时,没有找到匹配的 bean,并且定义了 geocodertravelDistanceCalculator bean。

ConditionalOnMissingBean 的 javadoc 提出以下建议:

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

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

【讨论】:

  • 自动配置类在我看来像 over-engineering,但感谢您的回答
猜你喜欢
  • 1970-01-01
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 2021-08-02
相关资源
最近更新 更多