Spring:如何在Profiles中做AND?

时间:2014-11-21 06:02:28

标签: java spring spring-profiles

Spring Profile注释允许您选择配置文件。但是,如果您阅读文档,它只允许您使用OR操作选择多个配置文件。如果您指定@Profile(" A"," B"),那么如果配置文件A或配置文件B处于活动状态,您的bean将会启动。

我们的用例不同,我们希望支持多种配置的TEST和PROD版本。因此,有时我们只想在配置文件TEST和CONFIG1都处于活动状态时自动装配bean。

有没有办法用Spring做到这一点?什么是最简单的方法?

7 个答案:

答案 0 :(得分:23)

由于Spring不提供开箱即用的AND功能。我建议采用以下策略:

目前@Profile注释具有条件注释@Conditional(ProfileCondition.class)。在ProfileCondition.class中,它遍历配置文件并检查配置文件是否处于活动状态。类似地,您可以创建自己的条件实现并限制注册bean。 e.g。

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;
    }

}

在你班上:

@Component
@Profile("dev")
@Conditional(value = { MyProfileCondition.class })
public class DevDatasourceConfig

注意:我没有检查所有角落情况(如空值,长度检查等)。但是,这个方向可能有所帮助。

答案 1 :(得分:9)

自从Spring 5.1(包含在Spring Boot 2.1中)以来,可以在配置文件字符串注释内使用配置文件表达式。所以:

Spring 5.1(Spring Boot 2.1)及更高版本中很容易:

form = FormGroup({}); // create your FormGroup here or if you already have it, just check 
if (this.form.dirty) {}

Spring 4.x和5.0.x

  • 方法1:answered by @Mithun,当您同时使用Spring Bean的@Component @Profile("TEST & CONFIG1") public class MyComponent {} 类实现对Spring Bean进行注释时,它很好地涵盖了您在配置文件注释中将OR转换为AND的情况。但是我想提供另一种没有人提出的具有赞成和反对意见的方法。

  • 方法2: 只需使用Condition并创建所需数量的@Conditional实现即可。它的缺点是必须创建与组合一样多的实现,但是在我看来,如果您没有太多的组合,则它是一种更为简洁的解决方案,它提供了更大的灵活性,并提供了实现更复杂的逻辑解决方案的机会。

方法2 的实现如下。

您的春豆:

Condition

@Component @Conditional(value = { TestAndConfig1Profiles.class }) public class MyComponent {} 实现:

TestAndConfig1Profiles

使用这种方法,您可以轻松涵盖更复杂的逻辑情况,例如:

(测试和配置1)| (测试和配置3)

只想为您的问题提供更新的答案,并补充其他答案。

答案 2 :(得分:7)

@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();
}

}

无法在评论中发布。

答案 3 :(得分:6)

另一个选择是播放@Profile注释所允许的类/方法级别。不像实施MyProfileCondition那样灵活,但如果适合你的情况则快速而干净。

e.g。这不会在FAST&amp; amp; DEV都是活动的,但只有DEV是:

@Configuration
@Profile("!" + SPRING_PROFILE_FAST)
public class TomcatLogbackAccessConfiguration {

    @Bean
    @Profile({SPRING_PROFILE_DEVELOPMENT, SPRING_PROFILE_STAGING})
    public EmbeddedServletContainerCustomizer containerCustomizer() {

答案 4 :(得分:6)

我改进了@ rozhoc的答案,因为答案没有考虑到这样一个事实:没有个人档案等同于&#39;默认&#39;当谈到使用@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;
}

以下是您实际使用它的方式,以防令人困惑:

@Profile({"!default", "!a"})
@Conditional(value={AndProfilesCondition.class})

答案 5 :(得分:5)

如果您已使用@Profile注释标记了配置类或bean方法,则可以使用Environment.acceptsProfiles()

检查其他配置文件(例如AND条件)
@Autowired Environment env;

@Profile("profile1")
@Bean
public MyBean myBean() {
    if( env.acceptsProfiles("profile2") ) {
        return new MyBean();
    }
    else {
        return null;
    }
}

答案 6 :(得分:5)

另一种技巧但可能在许多情况下工作的是在@Configuration上放置@Profile注释,在@Bean上放置另一个@Profile - 它在基于java的spring配置中的2个配置文件之间创建逻辑AND。

@Configuration
@Profile("Profile1")
public class TomcatLogbackAccessConfiguration {

   @Bean
   @Profile("Profile2")
   public EmbeddedServletContainerCustomizer containerCustomizer() {