当多个配置文件不活动时如何有条件地声明 Bean?
Posted
技术标签:
【中文标题】当多个配置文件不活动时如何有条件地声明 Bean?【英文标题】:How to conditionally declare Bean when multiple profiles are not active? 【发布时间】:2016-05-27 12:46:05 【问题描述】:在我的 Spring-Boot-App 中,我想根据(未)加载的 spring-profiles 有条件地声明一个 Bean。
条件:
Profile "a" NOT loaded
AND
Profile "b" NOT loaded
到目前为止我的解决方案(有效):
@Bean
@ConditionalOnExpression("#!environment.getProperty('spring.profiles.active').contains('a') && !environment.getProperty('spring.profiles.active').contains('b')")
public MyBean myBean()/*...*/
有没有更优雅(更短)的方式来解释这种情况? 尤其是这里想摆脱Spring Expression Language的使用。
【问题讨论】:
【参考方案1】:自 Spring 5.1.4(并入 Spring Boot 2.1.2)以来,可以在配置文件字符串注释中使用配置文件表达式。所以:
在 Spring 5.1.4 (Spring Boot 2.1.2) 及更高版本中,它很简单:
@Component
@Profile("!a & !b")
public class MyComponent
在 Spring 4.x 和 5.0.x 中:
这个 Spring 版本有很多方法,每一种都有其优缺点。当没有太多组合可以涵盖时,我个人喜欢 @Stanislav answer 和 @Conditional
注释。
其他方法可以在这个类似的问题中找到:
Spring Profile - How to include AND condition for adding 2 profiles?
Spring: How to do AND in Profiles?
【讨论】:
这太棒了!现在这应该是公认的答案:-) 对于这种特定情况(双重否定),您需要特别确定它是 spring-core: 5.1.4+ / spring-boot: 2.1.2+,因为有一个讨厌的错误直到这些版本:github.com/spring-projects/spring-framework/issues/22138【参考方案2】:如果您只有一个配置文件,您可以简单地使用带有 not 运算符的 @Profile
注释。它也接受多个配置文件,但带有OR
条件。
因此,替代解决方案是使用带有 @Conditional
注释的自定义 Condition
。像这样:
public class SomeCustomCondition implements Condition
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
// Return true if NOT "a" AND NOT "b"
return !context.getEnvironment().acceptsProfiles("a")
&& !context.getEnvironment().acceptsProfiles("b");
然后用它注释你的方法,比如:
@Bean
@Conditional(SomeCustomCondition.class)
public MyBean myBean()/*...*/
【讨论】:
谢谢,我喜欢这种方法。 +1 使用Condition
。在我接受这个答案之前,我将等待一段时间。也许还有另一个更优雅的建议;-)(但我不这么认为)
我在上面尝试了同样的方法,但由于某种原因它忽略了@Conditional 注释。有什么建议吗?
acceptProfiles () 是一个可变参数。我们可以一次指定多个配置文件,例如 return !context.getEnvironment().acceptsProfiles("a", "b") ;【参考方案3】:
我更喜欢这种更详细但仅适用于两个配置文件的解决方案:
@Profile("!a")
@Configuration
public class NoAConfig
@Profile("!b")
@Configuration
public static class NoBConfig
@Bean
public MyBean myBean()
return new MyBean();
【讨论】:
【参考方案4】:很遗憾,我没有为您提供更短的解决方案,但如果您的情况适合为每个配置文件创建相同的 bean,您可以考虑以下方法。
@Configuration
public class MyBeanConfiguration
@Bean
@Profile("a")
public MyBean myBeanForA() /*...*/
@Bean
@Profile("b")
public MyBean myBeanForB() /*...*/
@Bean
@ConditionalOnMissingBean(MyBean.class)
public MyBean myBeanForOthers() /*...*/
【讨论】:
谢谢,但这不适合我。假设我们加载了 Profilea
AND b
,那么在这种情况下 MyBean
被加载了两次,通常我们不希望这种行为。第二件事是,这不是我的问题的解决方案。当两个配置文件都没有加载时,MyBean
也不应该被声明。
像这样使用@ConditionalOnMissingBean
是危险的。为了安全起见,它应该只用于自动配置类
谢谢你们的cmets,伙计们。我曾经使用配置文件作为与环境相关的配置属性束,例如“dev”、“test”等。实际上,如果同时加载了两个配置文件,则无法使用这种方法。【参考方案5】:
@Stanislav answer 的变体,可读性更高,适用于最高 5.0.x / Spring Boot 2.0.x 的 Spring 版本。
public class SomeCustomCondition implements Condition
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata)
final Environment environment = context.getEnvironment();
// true if profile is NOT "a" AND NOT "b" AND NOT "c"
return !environment.acceptsProfiles("a", "b", "c");
对于较新版本的 Spring / Spring Boot,请参阅f-CJ answer。
【讨论】:
以上是关于当多个配置文件不活动时如何有条件地声明 Bean?的主要内容,如果未能解决你的问题,请参考以下文章