Java Spring:如何在配置文件中执行和?

Java Spring:如何在配置文件中执行和?,java,spring,spring-profiles,Java,Spring,Spring Profiles,Spring注释允许您选择纵断面。但是,如果您阅读文档,它只允许您使用或操作选择多个配置文件。如果您指定@Profile(“A”、“B”),那么如果Profile A或Profile B处于活动状态,您的bean将启动 我们的用例不同,我们希望支持多种配置的测试和产品版本。因此,有时我们只想在概要文件TEST和CONFIG1都处于活动状态时自动连接bean 有什么办法可以用弹簧来做吗?最简单的方法是什么?因为Spring不提供现成的和功能。我建议采取以下战略: 当前,@Profile注释有一个条

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

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


有什么办法可以用弹簧来做吗?最简单的方法是什么?

因为Spring不提供现成的和功能。我建议采取以下战略:

当前,
@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;
    }

}

注意:我没有检查所有的角点情况(如null、长度检查等)。但是,这个方向可能会有所帮助

对@Mithun answer的一点改进版本:

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

}
public类和profiles条件实现条件{
公共静态最终字符串VALUE=“VALUE”;
公共静态最终字符串DEFAULT\u PROFILE=“DEFAULT”;
@凌驾
公共布尔匹配(最终条件上下文、最终注释类型元数据){
if(context.getEnvironment()==null){
返回true;
}
MultiValueMap attrs=metadata.getAllAnnotationAttributes(Profile.class.getName());
if(attrs==null){
返回true;
}
字符串[]activeProfiles=context.getEnvironment().getActiveProfiles();
String[]definedProfiles=(String[])attrs.getFirst(值);
Set allowedProfiles=新哈希集(1);
Set restrictedProfiles=新哈希集(1);
用于(字符串nextDefinedProfile:definedProfiles){
如果(!nextDefinedProfile.isEmpty()&&nextDefinedProfile.charAt(0)=='!'){
添加(nextDefinedProfile.substring(1,nextDefinedProfile.length());
继续;
}
allowedProfiles.add(nextDefinedProfile);
}
int activeAllowedCount=0;
用于(字符串nextActiveProfile:activeProfiles){
//默认配置文件处于活动状态且允许的配置文件为空时快速退出
if(默认_PROFILE.equals(nextActiveProfile)&&allowedProfiles.isEmpty()){
继续;
}
//当一个活动配置文件受到限制时快速退出
if(restrictedProfiles.contains(nextActiveProfile)){
返回false;
}
//如果没有允许的配置文件,请继续操作(只需检查是否没有活动的受限配置文件)
if(allowedProfiles.isEmpty()){
继续;
}
if(allowedProfiles.contains(nextActiveProfile)){
activeAllowedCount++;
}
}
return activeAllowedCount==allowedProfiles.size();
}
}

无法将其发布到评论中。

如果您已经使用@Profile注释标记了配置类或bean方法,那么使用
Environment.acceptsProfiles()检查其他配置文件(例如for和condition)就很简单了。


还有一个选择是在
@Profile
注释所允许的类/方法级别上玩。不像实现
MyProfileCondition
那样灵活,但如果适合您的情况,则可以快速、干净地实现

e、 g.当FAST和DEV都处于活动状态时,这不会启动,但如果只有DEV处于以下状态,则会启动:

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

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

我改进了@rozhoc的答案,因为这个答案没有考虑到在使用@profile时没有任何配置文件等同于“默认”这一事实。另外,我想要的条件是
!默认值&!@rozhoc的代码未正确处理的
。最后,为了简洁起见,我使用了一些Java8并只显示
匹配的方法

@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注释放在@Configuration上,另一个@Profile放在@Bean上——在基于java的spring配置中创建两个配置文件之间的逻辑and

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

   @Bean
   @Profile("Profile2")
   public EmbeddedServletContainerCustomizer containerCustomizer() {
由于Spring5.1(包含在SpringBoot2.1中),所以可以在配置文件字符串注释中使用配置文件表达式。因此:

Spring 5.1(Spring Boot 2.1)及更高版本中操作非常简单:

@Component
@Profile("TEST & CONFIG1")
public class MyComponent {}
弹簧4.x和5.0.x

  • 方法1:,它完美地涵盖了您在使用Springbean的
    条件
    类实现注释Springbean时在概要文件注释中转换或转换的情况。但我想提供另一种没有人提出的方法,它既有赞成的,也有反对的

  • 方法2: 只需使用
    @Conditional
    并根据需要创建尽可能多的
    条件
    实现。它的缺点是必须创建与组合一样多的实现,但如果没有很多组合,在我看来,它是一个更简洁的解决方案,它提供了更大的灵活性和实现更复杂逻辑解决方案的机会

办法2的执行情况如下

你的春豆:

@Component
@Conditional(value = { TestAndConfig1Profiles.class })
public class MyComponent {}
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");
    }
}
通过这种方法,您可以轻松地涵盖更复杂的逻辑情况,例如:

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


只是想对你的问题给出一个更新的答案,并补充其他答案。

在文档中,它被称为
和/或
@Profile(“a”、“b”)
的行为。这不是你要找的吗?docs-
同样,如果@Component或@Configuration类用@Profile({“p1”,“p2”})标记,则该类将不会被注册/处理,除非已激活配置文件“p1”和/或“p2”。
@JavaBond,这意味着它是“或”运算符而不是“和”。他们只是想明确指出
@Component
@Profile("TEST & CONFIG1")
public class MyComponent {}
@Component
@Conditional(value = { TestAndConfig1Profiles.class })
public class MyComponent {}
public class TestAndConfig1Profiles implements Condition {
    @Override
    public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().acceptsProfiles("TEST")
                    && context.getEnvironment().acceptsProfiles("CONFIG1");
    }
}