【发布时间】:2018-10-21 13:10:09
【问题描述】:
我有一个Spring Boot 1.5.x 项目,其中一些@Component 依赖于其他@Component,最终沿着依赖链,可以使用@ConditionalOnProperty 完全启用或禁用一些@Component。
我使用@ConditionalOnBean 来避免实例化@Component,它依赖于由于缺少properties 而未实例化的其他@Component。
但是,它只适用于直接依赖,不适用于传递依赖,但我不明白为什么。
让我试着用一个简单的例子来解释一下。
考虑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" }
}
}
以下application.yml:
service:
a: false
b: true
c: true
然后 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.
这是自动配置的结果:
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)
但是,使用以下application.yml:
service:
a: true
b: false
c: true
然后一切正常,只有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)
application.yml:
bean:
a: false
b: true
c: true
我没有实例化 BeanA、BeanB 或 BeanC 类型的 bean。
这是自动配置的结果:
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)
我已经设置了一个示例 repo,其中包含要重现的测试:https://github.com/gotson/spring-transitive
【问题讨论】:
-
有趣的是,我可以看到的第一个服务差异是
ConditionalOnProperty在PARSE_CONFIGURATION阶段评估,ConditionalOnBean在REGISTER_BEAN评估。 ServiceA 只有 onProperty 它没有注册,但 ServiceB 两者都有,而且 Spring,在寻找注册候选人时只检查PARSE_CONFIGURATION,这导致serviceB在 beanFactory 中注册,并且 onBeanCondition 在 beanFactory 中检查现有的潜在 bean匹配,在真正创建它们之前。 -
我添加了自动配置报告的详细信息。很奇怪,ServiceC 是
@ConditionalOnBean上的匹配项,而 ServiceB 不是。
标签: java spring spring-boot kotlin