Java Spring:如何在 Profiles 中做 AND?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27055072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring: How to do AND in Profiles?
提问by Artem
Spring Profileannotation allows you to select profiles. However if you read documentation it only allows you to select more than one profile with OR operation. If you specify @Profile("A", "B") then your bean will be up if either profile A or profile B is active.
Spring Profile注释允许您选择配置文件。但是,如果您阅读文档,它只允许您使用 OR 操作选择多个配置文件。如果您指定 @Profile("A", "B") 那么如果配置文件 A 或配置文件 B 处于活动状态,则您的 bean 将启动。
Our use case is different we want to support TEST and PROD versions of multiple configurations. Therefore sometimes we want to autowire the bean only if both profiles TEST and CONFIG1 are active.
我们的用例不同,我们希望支持多种配置的 TEST 和 PROD 版本。因此,有时我们希望仅当配置文件 TEST 和 CONFIG1 都处于活动状态时才自动装配 bean。
Is there any way to do it with Spring? What would be the simplest way?
有没有办法用Spring做到这一点?最简单的方法是什么?
采纳答案by Mithun
Since Spring does not provide the AND feature out of the box. I would suggest the following strategy:
由于 Spring 不提供开箱即用的 AND 功能。我会建议以下策略:
Currently @Profile
annotation has a conditional annotation @Conditional(ProfileCondition.class)
. In ProfileCondition.class
it iterates through the profiles and checks if the profile is active. Similarly you could create your own conditional implementation and restrict registering the bean. e.g.
目前@Profile
annotation 有一个条件 annotation @Conditional(ProfileCondition.class)
。在ProfileCondition.class
迭代了通过配置文件和检查配置文件处于活动状态。同样,您可以创建自己的条件实现并限制注册 bean。例如
public class MyProfileCondition implements Condition {
@Override
public boolean matches(final ConditionContext context,
final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
final MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (final Object value : attrs.get("value")) {
final String activeProfiles = context.getEnvironment().getProperty("spring.profiles.active");
for (final String profile : (String[]) value) {
if (!activeProfiles.contains(profile)) {
return false;
}
}
}
return true;
}
}
return true;
}
}
In your class:
在你的课堂上:
@Component
@Profile("dev")
@Conditional(value = { MyProfileCondition.class })
public class DevDatasourceConfig
NOTE: I have not checked for all the corner cases (like null, length checks etc). But, this direction could help.
注意:我没有检查所有的极端情况(如空值、长度检查等)。但是,这个方向可能会有所帮助。
回答by Vladimir Rozhkov
A little bit improved version of @Mithun answer:
@Mithun 答案的一点改进版本:
public class AndProfilesCondition implements Condition {
public static final String VALUE = "value";
public static final String DEFAULT_PROFILE = "default";
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() == null) {
return true;
}
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs == null) {
return true;
}
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
Set<String> allowedProfiles = new HashSet<>(1);
Set<String> restrictedProfiles = new HashSet<>(1);
for (String nextDefinedProfile : definedProfiles) {
if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
continue;
}
allowedProfiles.add(nextDefinedProfile);
}
int activeAllowedCount = 0;
for (String nextActiveProfile : activeProfiles) {
// quick exit when default profile is active and allowed profiles is empty
if (DEFAULT_PROFILE.equals(nextActiveProfile) && allowedProfiles.isEmpty()) {
continue;
}
// quick exit when one of active profiles is restricted
if (restrictedProfiles.contains(nextActiveProfile)) {
return false;
}
// just go ahead when there is no allowed profiles (just need to check that there is no active restricted profiles)
if (allowedProfiles.isEmpty()) {
continue;
}
if (allowedProfiles.contains(nextActiveProfile)) {
activeAllowedCount++;
}
}
return activeAllowedCount == allowedProfiles.size();
}
}
Was unable to post it in the comments.
无法在评论中发布它。
回答by Sergey Shcherbakov
If you have already marked a configuration class or bean method with @Profile annotation, it is simple to check for additional profiles (e.g. for AND condition) with Environment.acceptsProfiles()
如果你已经用@Profile 注释标记了一个配置类或 bean 方法,那么检查其他配置文件(例如对于 AND 条件)很简单 Environment.acceptsProfiles()
@Autowired Environment env;
@Profile("profile1")
@Bean
public MyBean myBean() {
if( env.acceptsProfiles("profile2") ) {
return new MyBean();
}
else {
return null;
}
}
回答by coolnodje
Yet another option is to play on the Class/Method level allowed by the @Profile
annotation. Not as flexible as implementing MyProfileCondition
but quick and clean if it suits your case.
另一种选择是在@Profile
注释允许的类/方法级别上播放。不像实施那样灵活,MyProfileCondition
但如果适合您的情况,则快速而干净。
e.g. this won't start when FAST & DEV are both active, but will if only DEV is:
例如,当 FAST 和 DEV 都处于活动状态时,这不会启动,但如果只有 DEV 是:
@Configuration
@Profile("!" + SPRING_PROFILE_FAST)
public class TomcatLogbackAccessConfiguration {
@Bean
@Profile({SPRING_PROFILE_DEVELOPMENT, SPRING_PROFILE_STAGING})
public EmbeddedServletContainerCustomizer containerCustomizer() {
回答by RubesMN
I improved @rozhoc's answer since that answer did not account for the fact that no profile is equivalent to 'default' when it comes to using @Profile. Also, conditions that I wanted were !default && !a
which @rozhoc's code did not handle properly. Finally I used some Java8 and show only the matches
method for brevity.
我改进了@rozhoc 的答案,因为该答案没有说明在使用 @Profile 时没有配置文件等同于“默认”的事实。另外,我想要的条件是!default && !a
@rozhoc 的代码没有正确处理。最后我使用了一些 Java8 并且matches
为了简洁只展示了方法。
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() == null) {
return true;
}
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs == null) {
return true;
}
Set<String> activeProfilesSet = Arrays.stream(context.getEnvironment().getActiveProfiles()).collect(Collectors.toSet());
String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
Set<String> allowedProfiles = new HashSet<>(1);
Set<String> restrictedProfiles = new HashSet<>(1);
if (activeProfilesSet.size() == 0) {
activeProfilesSet.add(DEFAULT_PROFILE); // no profile is equivalent in @Profile terms to "default"
}
for (String nextDefinedProfile : definedProfiles) {
if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
continue;
}
allowedProfiles.add(nextDefinedProfile);
}
boolean allowed = true;
for (String allowedProfile : allowedProfiles) {
allowed = allowed && activeProfilesSet.contains(allowedProfile);
}
boolean restricted = true;
for (String restrictedProfile : restrictedProfiles) {
restricted = restricted && !activeProfilesSet.contains(restrictedProfile);
}
return allowed && restricted;
}
Here is how you actually use it in case that was confusing as well:
以下是您实际使用它的方法,以防万一也令人困惑:
@Profile({"!default", "!a"})
@Conditional(value={AndProfilesCondition.class})
回答by Martin Tlacha?
Another kind of trick but might work in many scenarios is put @Profile annotation on @Configuration and the other @Profile on @Bean - that creates logical AND between 2 profiles in java-based spring config.
另一种技巧但可能在许多情况下有效是将@Profile 注释放在@Configuration 上,将另一个@Profile 放在@Bean 上——这在基于java 的spring 配置中的2 个配置文件之间创建逻辑AND。
@Configuration
@Profile("Profile1")
public class TomcatLogbackAccessConfiguration {
@Bean
@Profile("Profile2")
public EmbeddedServletContainerCustomizer containerCustomizer() {
回答by f-CJ
Since Spring 5.1 (incorporated in Spring Boot 2.1) it is possible to use a profile expression inside profile string annotation. So:
从 Spring 5.1(包含在 Spring Boot 2.1 中)开始,可以在配置文件字符串注释中使用配置文件表达式。所以:
In Spring 5.1 (Spring Boot 2.1) and aboveit is as easy as:
在Spring 5.1 (Spring Boot 2.1) 及更高版本中,它很简单:
@Component
@Profile("TEST & CONFIG1")
public class MyComponent {}
Spring 4.x and 5.0.x:
弹簧 4.x 和 5.0.x:
Approach 1: answered by @Mithun, it covers perfectly your case of converting OR into AND in your profile annotation whenever you annotate the Spring Bean also with his
Condition
class implementation. But I want to offer another approach that nobody proposed that has its pro's and con's.Approach 2: Just use
@Conditional
and create as manyCondition
implementations as combinations needed. It has the con of having to create as many implementations as combinations but if you don't have many combinations, in my opinion, it is a more concise solution and it offers more flexibility and the chance of implementing more complex logical resolutions.
方法 1:由 @Mithun 回答,它完美地涵盖了您在配置文件注释中将 OR 转换为 AND 的情况,无论何时您还使用他的
Condition
类实现对 Spring Bean 进行注释。但我想提供另一种没有人提出的方法,它有其优点和缺点。方法 2:只需根据需要使用
@Conditional
和创建尽可能多的Condition
实现。它的缺点是必须创建与组合一样多的实现,但如果你没有很多组合,在我看来,它是一个更简洁的解决方案,它提供了更多的灵活性和实现更复杂逻辑解决方案的机会。
The implementation of Approach 2would be as follows.
方法 2的实施如下。
Your Spring Bean:
你的春天豆:
@Component
@Conditional(value = { TestAndConfig1Profiles.class })
public class MyComponent {}
TestAndConfig1Profiles
implementation:
TestAndConfig1Profiles
执行:
public class TestAndConfig1Profiles implements Condition {
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
return context.getEnvironment().acceptsProfiles("TEST")
&& context.getEnvironment().acceptsProfiles("CONFIG1");
}
}
With this approach you could easily cover more complex logical situations like for example:
使用这种方法,您可以轻松涵盖更复杂的逻辑情况,例如:
(TEST & CONFIG1) | (TEST & CONFIG3)
(测试和配置 1) | (测试和配置3)
Just wanted to give an updated answer to your question and complement other answers.
只是想为您的问题提供更新的答案并补充其他答案。