我可以否定 (!) 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")
是否可以配置一个 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")