【问题标题】:How to conditionally declare Bean when multiple profiles are not active?当多个配置文件不活动时如何有条件地声明 Bean?
【发布时间】: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的使用。

【问题讨论】:

    标签: spring spring-boot


    【解决方案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?

    【讨论】:

    【解决方案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

      【讨论】:

        猜你喜欢
        • 2012-11-14
        • 2016-01-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-05
        • 2011-07-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多