Spring Boot与@ConditionalOnBean的传递@Component依赖关系

Gau*_*ier 6 java spring kotlin spring-boot

我有一个Spring Boot 1.5.x项目,其中一些@Component依赖于其他项目@Component,最终沿着依赖项链,有些@Component可以完全启用或禁用@ConditionalOnProperty.

我正在使用@ConditionalOnBean以避免实例化@Component,这取决于@Component因缺失而未实例化的其他实例properties.

但是,它只适用于直接依赖,而不适用于传递依赖,但我无法理解为什么.

让我试着用一个简单的例子来解释.

考虑MyServices.kt:

private val logger = KotlinLogging.logger {}

class MyServices

@ConditionalOnProperty("service.a")
@Service
class ServiceA {
    init {
        logger.info { "A SERVICE" }
    }
}

@ConditionalOnBean(ServiceA::class)
@ConditionalOnProperty("service.b")
@Service
class ServiceB(
        private val serviceA: ServiceA
) {
    init {
        logger.info { "B SERVICE depends on $serviceA" }
    }
}

@ConditionalOnBean(ServiceB::class)
@ConditionalOnProperty("service.c")
@Service
class ServiceC(
        private val serviceB: ServiceB
) {
    init {
        logger.info { "C Service depends on $serviceB" }
    }
}
Run Code Online (Sandbox Code Playgroud)

具有以下内容application.yml:

service:
  a: false
  b: true
  c: true
Run Code Online (Sandbox Code Playgroud)

然后Spring在启动时崩溃,具体如下:

**************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in org.gotson.transitivebeandependencies.ServiceC required a bean of type 'org.gotson.transitivebeandependencies.ServiceB' that could not be found.


Action:

Consider defining a bean of type 'org.gotson.transitivebeandependencies.ServiceB' in your configuration.
Run Code Online (Sandbox Code Playgroud)

以下是自动配置的结果:

Positive matches:

ServiceC matched:
      - @ConditionalOnProperty (service.c) matched (OnPropertyCondition)
      - @ConditionalOnBean (types: org.gotson.transitivebeandependencies.ServiceB; SearchStrategy: all) found bean 'serviceB' (OnBeanCondition)

Negative matches:

ServiceA:
      Did not match:
         - @ConditionalOnProperty (service.a) found different value in property 'service.a' (OnPropertyCondition)

   ServiceB:
      Did not match:
         - @ConditionalOnBean (types: org.gotson.transitivebeandependencies.ServiceA; SearchStrategy: all) did not find any beans (OnBeanCondition)
      Matched:
         - @ConditionalOnProperty (service.b) matched (OnPropertyCondition)
Run Code Online (Sandbox Code Playgroud)

但是,具有以下内容application.yml:

service:
  a: true
  b: false
  c: true
Run Code Online (Sandbox Code Playgroud)

然后一切正常,只有一个实例ServiceA被实例化,而没有ServiceB也不会ServiceC被创建的Bean.


相同的行为@Bean而不是@Component按预期工作.

MyBeans.kt:

private val logger = KotlinLogging.logger {}

@Configuration
class MyBeans {

    @ConditionalOnProperty("bean.a")
    @Bean
    fun beanA(): BeanA {
        logger.info { "A BEAN" }
        return BeanA("beanA")
    }

    @ConditionalOnBean(BeanA::class)
    @ConditionalOnProperty("bean.b")
    @Bean
    fun beanB(beanA: BeanA): BeanB {
        logger.info { "B BEAN depends on $beanA" }
        return BeanB("beanB")
    }

    @ConditionalOnBean(BeanB::class)
    @ConditionalOnProperty("bean.c")
    @Bean
    fun beanC(beanB: BeanB): BeanC {
        logger.info { "C BEAN depends on $beanB" }
        return BeanC("beanC")
    }

}

data class BeanA(val name: String)
data class BeanB(val name: String)
data class BeanC(val name: String)
Run Code Online (Sandbox Code Playgroud)

application.yml:

bean:
  a: false
  b: true
  c: true
Run Code Online (Sandbox Code Playgroud)

我没有得到任何类型的豆BeanA,BeanBBeanC实例化.

以下是自动配置的结果:

Negative matches:

MyBeans#beanA:
      Did not match:
         - @ConditionalOnProperty (bean.a) found different value in property 'bean.a' (OnPropertyCondition)

   MyBeans#beanB:
      Did not match:
         - @ConditionalOnBean (types: org.gotson.transitivebeandependencies.BeanA; SearchStrategy: all) did not find any beans (OnBeanCondition)
      Matched:
         - @ConditionalOnProperty (bean.b) matched (OnPropertyCondition)

   MyBeans#beanC:
      Did not match:
         - @ConditionalOnBean (types: org.gotson.transitivebeandependencies.BeanB; SearchStrategy: all) did not find any beans (OnBeanCondition)
      Matched:
         - @ConditionalOnProperty (bean.c) matched (OnPropertyCondition)
Run Code Online (Sandbox Code Playgroud)

我已经设置了一个带有测试的样本repo来重现:https://github.com/gotson/spring-transitive

Ste*_*oll 4

@ConditionalOnBean是 bean 注册阶段检查,因此需要概述ApplicationContext. Bean 可以使用常规方法以标准方式注册@Bean,公开与方法的返回类型相同的目标类型。您可能还有FactoryBean更复杂的逻辑,这可能会导致我们必须处理奇异的设置。

无论如何,订购是关键。如果您希望 Bean 类型匹配正常工作,则必须按给定顺序处理配置类。如果您有一个C1配置类,A仅当 beanB可用并且该 bean 是由 贡献时才提供 bean C2C2 则必须先运行

Spring Boot 有两个步骤的解析阶段:首先我们解析所有用户的配置。完成后,我们解析自动配置的 bean 定义。自动配置本身是有序的(使用@AutoConfigureBefore, @AutoConfigureAfter)。这样,我们可以保证,如果您将@ConditionalOnBean自动配置,它将按照用户配置的预期进行处理。如果您依赖另一个自动配置提供的东西,您可以使用这些注释轻松订购它。

您的设置完全避开了顺序,因此如果顺序正确则它可以工作,如果顺序不正确则不能。Javadoc@ConditionalOnBean明确指出

强烈建议仅在自动配置类上使用此条件。

如果您想了解更多信息, youtube 上有一个 3 小时的大学课程,涵盖了这个主题。