我可以否定 (!) spring 个配置文件的集合吗?

Can I negate (!) a collection of spring profiles?

是否可以配置一个 bean,使其不会被一组配置文件使用?目前我可以做到这一点(我相信):

@Profile("!dev, !qa, !local")

是否有更简洁的符号来实现此目的?假设我有很多个人资料。另外,如果我有一些服务(或其他)的模拟和具体实现,我可以只注释其中一个,并假设另一个将在所有其他情况下使用吗?换句话说,是否需要,例如:

@Profile("dev, prof1, prof2")
public class MockImp implements MyInterface {...}

@Profile("!dev, !prof1, !prof2") //assume for argument sake that there are many other profiles
public class RealImp implements MyInterface {...}

我可以只注释其中一个,而在另一个上贴上 @Primary 注释吗?

本质上我想要这个:

@Profile("!(dev, prof1, prof2)")

提前致谢!

据我了解,您想要做的是能够用一些 stub/mock beans 替换您的一些 beans 以用于特定配置文件。有两种方法可以解决这个问题:

  • 排除相应配置文件不需要的 beans,默认情况下包括所有其他内容
  • 仅包括每个配置文件所需的 bean

第一种方案可行,但难度较大。这是因为在 @Profile 注释中提供多个配置文件时 Spring 的默认行为是 OR 条件(而不是您的情况下需要的 AND )。 Spring 的这种行为更直观,因为理想情况下,每个配置文件应对应于应用程序的每个配置(生产、单元测试、集成测试等),因此每次只有一个配置文件处于活动状态。这就是 OR 比配置文件之间的 AND 更有意义的原因。因此,您可以解决此限制,可能是通过嵌套配置文件,但会使您的配置变得非常复杂且难以维护。

因此,我建议您采用第二种方法。应用程序的每个配置都有一个配置文件。每个配置都相同的所有 bean 都可以驻留在没有指定 @Profile 的 class 中。因此,这些 bean 将由所有配置文件实例化。对于每个不同配置应该不同的其余 bean,您应该创建一个单独的 @Configuration class(对于每个 Spring 配置文件),让所有这些都带有 @Profile设置为相应的配置文件。这样,在每个案例中都可以很容易地追踪注入的内容。

这应该如下所示:

@Profile("dev")
public class MockImp implements MyInterface {...}

@Profile("prof1")
public class MockImp implements MyInterface {...}

@Profile("prof2")
public class MockImp implements MyInterface {...}

@Profile("the-last-profile") //you should define an additional profile, not rely on excluding as described before
public class RealImp implements MyInterface {...}

最后,@Primary 注解用于覆盖现有 bean。当有2个相同类型的bean时,如果没有@Primary注解,你会得到Spring的实例化错误。如果您为其中一个 bean 定义了一个 @Primary 注释,则不会出现错误,并且该 bean 将被注入到需要此类型的任何地方(另一个将被忽略)。如您所见,这仅在您只有一个配置文件时才有用。不然这个作为首选也会变复杂

TL;DR:是的,你可以。对于每种类型,为每个配置文件定义一个 bean,并添加一个仅包含此配置文件的 @Profile 注释。

简短的回答是:您不能在 Spring 5.1 之前的 Spring 版本(即 2.1 之前的 Spring Boot 版本)。

但是由于 @Conditional 注释,存在一个巧妙的解决方法。

创建条件匹配器:

public static abstract class ProfileCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        if (matchProfiles(conditionContext.getEnvironment())) {
            return ConditionOutcome.match("A local profile has been found.");
        }
        return ConditionOutcome.noMatch("No local profiles found.");
    }

    protected static abstract boolean matchProfiles(final Environment environment);
}

public class DevProfileCondition extends ProfileCondition {
   protected boolean matchProfiles(final Environment environment) {    
        return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
            return prof.equals("dev") || prof.equals("prof1") || prof.equals("prof2");
        });
    }
}

public static class ProdProfileCondition extends ProfileCondition {
   protected boolean matchProfiles(final Environment environment) {    
        return Arrays.stream(environment.getActiveProfiles()).anyMatch(prof -> {
            return (!prof.equals("dev") && !prof.equals("prof1") && !prof.equals("prof2"));
        });
    }
}

使用它

@Conditional(value = {DevProfileCondition.class})
public class MockImpl implements MyInterface {...}

@Conditional(value = {ProdProfileCondition.class})
public class RealImp implements MyInterface {...}

但是,此方法需要 Spring引导。

自 Spring 5.1(包含在 Spring Boot 2.1 中)可以使用 profile 表达式 inside profile string annotation(详见Profile.of(..)中的描述)。

因此,要从某些配置文件中排除您的 bean,您可以使用如下表达式:

@Profile("!dev & !prof1 & !prof2")

也可以使用其他逻辑运算符,例如:

@Profile("test | local")