我可以否定(!)一系列弹簧轮廓?

时间:2017-04-02 13:27:25

标签: java spring spring-boot config

是否可以以一种不被一组配置文件使用的方式配置bean?目前我可以这样做(我相信):

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

是否有更简洁的符号来实现这一目标?我们假设我有很多个人资料。另外,如果我有一个Mock和一些服务(或其他)的具体实现,我可以只注释其中一个,并假设另一个将用于所有其他情况?换句话说,这是必要的:

@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)")

提前致谢!

4 个答案:

答案 0 :(得分:16)

自Spring 5.1 (自Spring Boot 2.1以来)开始,可以在配置文件字符串注释内使用 profile表达式(有关详细信息,请参见Profile.of(..)中的描述详细信息。)

因此,要从特定配置文件中排除bean,可以使用如下表达式:

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

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

@Profile("test | local")

答案 1 :(得分:12)

简短的回答是:你不能。
但由于@Conditional注释,存在一个简洁的解决方法。

创建条件匹配器:

public 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 abstract boolean matchProfiles(final Environment environment);
}

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

public class ProdProfileCondition extends ProfileCondition {
   private 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 {...}

但是,这种方法需要Springboot。

答案 2 :(得分:1)

据我所知,你想要做的是能够用特定配置文件的一些存根/模拟bean替换你的一些bean。有两种方法可以解决这个问题:

  • 排除相应配置文件的不需要的bean,并默认包含其他所有内容
  • 仅包含每个配置文件所需的bean

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

因此,我建议你采用第二种方法。为您的应用程序的每个配置设置一个配置文件。对于每个配置,所有相同的bean都可以驻留在不指定@Profile的类中。因此,这些bean将由所有配置文件实例化。对于每个不同配置应该是不同的剩余bean,您应该为每个Spring配置文件创建一个单独的@Configuration类,并将所有这些类设置为相应的配置文件@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。如果有两个具有相同类型的bean,如果没有@Primary注释,则会从Spring获得实例化错误。如果为其中一个bean定义@Primary注释,则不会出现错误,并且会在需要此类型的任何位置注入此bean(另一个将被忽略)。如您所见,这仅在您拥有单个配置文件时才有用。否则,这也将成为第一选择。

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

答案 3 :(得分:-1)

您只需使用RealImp(或您自己的个人资料名称)标记@Profile("Production")课程,这样您就不需要指定所有其他个人资料。

  

我可以只注释其中一个,然后粘上一个@Primary注释   另一个呢?

是的,您也可以@Primary使用RealImp课程,但要确保在整个项目中使用相同的概念,即换句话说,如果你将一些bean实际实现类(用于实际生产环境)标记为@Profile("Production"),其中一些标记为@Primary,那么项目将处于混乱状态。