我有一个Spring Boot 1.5.x
项目,其中一些@Component
依赖于其他@Component
,并且最终沿着依赖链,可以使用{完全启用或禁用某些@Component
{1}}。
我正在使用@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" }
}
}
:
application.yml
然后Spring在启动时因以下内容崩溃:
service:
a: false
b: true
c: true
以下是自动配置的结果:
**************************
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
bean。
ServiceC
代替@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
类型的bean。
以下是自动配置的结果:
BeanC
我已经设置了一个样本仓库,其中包含要重现的测试:https://github.com/gotson/spring-transitive
答案 0 :(得分:2)
这里的问题是你对控件的定义。
对于@Service示例(false,true,true),您的控件属性确定服务是否已定义。 ServiceA未定义也未构建。 ServiceB已定义,但未构造,因为没有ServiceA定义。定义了ServiceC并尝试构建。如上所述,由于缺少ServiceB实例,ServiceC的构造失败。
对于@Bean示例(false,true,true),您的控件属性确定是否构造Bean。 BeanA没有构造,BeanB没有构造,因为没有BeanA,并且没有构造BeanC,因为没有BeanB。
对于@Service示例(true,false,true): ServiceA是定义和构造的,ServiceB既未定义也未构造,ServiceC已定义但未尝试构造,因为未定义ServiceB。
用@Bean修饰的方法生成一个bean,在配置阶段由Spring容器管理。
在组件扫描过程中将检测到用@Component(@Service)修饰的Java类,并将其注册为Spring bean。
因为@Service注释不像@Bean那样创建和注册组件,所以使用@ConditionalOnClass而不是@ConditionalOnBean可能会产生你想要的结果。关于这个注释有一些注意事项。
答案 1 :(得分:2)
@ConditionalOnBean
是一个bean注册阶段检查,因此需要概述ApplicationContext
中有效的bean。可以使用常规@Bean
以标准方式注册Bean,从而暴露与方法的返回类型相同的目标类型。您可能还有FactoryBean
具有更复杂的逻辑,这可能导致我们必须处理的异国情调设置。
无论如何,订购是关键。如果希望bean类型匹配正常工作,则必须按给定顺序处理配置类。如果你有一个C1
配置类,只有当bean A
可用并且所述bean由B
提供时才提供bean C2
,C2
必须先运行。
Spring Boot有两个步骤解析阶段:首先我们解析所有用户的配置。完成后,我们解析自动配置的bean定义。自动配置本身是有序的(使用@AutoConfigureBefore
,@AutoConfigureAfter
)。这样,我们可以保证,如果您将@ConditionalOnBean
置于自动配置中,它将按照用户配置的预期进行处理。如果您依赖其他自动配置所贡献的内容,您可以使用这些注释轻松订购。
您的设置完全避免了订购,因此如果订购是正确的,它可以正常工作,如果不是,则不工作。 @ConditionalOnBean
的Javadoc states that
强烈建议仅在自动配置类上使用此条件。
如果您想了解更多信息,可以参加3小时的大学课程,即这个主题on youtube。