当多个配置文件不活动时如何有条件地声明 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() /*...*/


【讨论】:

谢谢,但这不适合我。假设我们加载了 Profile a 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?的主要内容,如果未能解决你的问题,请参考以下文章

条件化bean

Spring - 多个配置文件处于活动状态

如何在过滤数据表jQuery中制作多个条件

当条件多个布局应用于单个活动时管理单个数据绑定对象

使用 f:ajax 时 JSF 不触发 bean setter

装配Bean