28

I've a Spring Boot application with different Profile setup : dev, prod, qc, console etc.

The two configuration classes are setup as follows. MyConfigurationA should be registered for all profiles except console. MyConfigurationB should be registered except for console and dev.

When I run the application with profile console, the MyConfigurationA doesn't get registered - which is fine. But MyConfigurationB gets registered - which I do not want. I've setup the @Profile annotation as follows to not register the MyConfigurationB for profile console and dev .

But the MyConfigurationB is getting registered when I run the application with profile console.

@Profile({ "!" + Constants.PROFILE_CONSOLE ,  "!" + Constants.PROFILE_DEVELOPMENT })

The documentation ( http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html) has an example of including one profile and excluding the other. In my example I'm excluding both as @Profile({"!p1", "!p2"}):

@Profile({"p1", "!p2"}), registration will occur if profile 'p1' is active OR if profile 'p2' is not active.

My question is : How can we skip registration of the configurations of both profiles? @Profile({"!p1", "!p2"}) is doing OR operation. We need AND operation here.


The code :

@Configuration
@Profile({ "!" + Constants.PROFILE_CONSOLE  })
public class MyConfigurationA {
    static{
        System.out.println("MyConfigurationA registering...");
    }
}

@Configuration
@Profile({ "!" + Constants.PROFILE_CONSOLE ,  "!" + Constants.PROFILE_DEVELOPMENT }) // doesn't exclude both, its OR condition
public class MyConfigurationB {
    static{
        System.out.println("MyConfigurationB registering...");
    }
}

public final class Constants {
    public static final String PROFILE_DEVELOPMENT = "dev";
    public static final String PROFILE_CONSOLE = "console";
    ...
}
gtiwari333
  • 24,554
  • 15
  • 75
  • 102

4 Answers4

44

@Profile({"!console", "!dev"}) means (NOT console) OR (NOT dev) which is true if you run your app with the profile 'console'.
To solve this you can create a custom Condition:

public class NotConsoleAndDevCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        return !environment.acceptsProfiles("console", "dev");
    }
}

And apply the condition via the @Conditional annotation to the Configuration:

@Conditional(NotConsoleAndDevCondition.class)
public class MyConfigurationB {
Cyril
  • 2,376
  • 16
  • 21
  • 6
    Instead of creating a custom condition, you can use profile expression(logical expression with profile as operands) with the @Profile annotation. ex: @Profile({"!p1 & !p2"}) Ref: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html – Ranjan Jan 20 '20 at 07:47
  • As of latest spring updates @RanjanMohanty's answer is concise, logical and works as intended – ashfaq Feb 19 '20 at 18:25
24

Starting with Spring 5.1 you can use expressions in @Profile annotation. Read more in the @Profile documentation. Example:

@Configuration
@Profile({ "!console & !dev" }) 
public class MyConfigurationB {
    static{
        System.out.println("MyConfigurationB registering...");
    }
}
t0r0X
  • 4,212
  • 1
  • 38
  • 34
xyz
  • 812
  • 9
  • 18
  • 1
    This approach is documented but does not seem to work as advertised – Fritz Duchardt Jan 29 '20 at 13:08
  • 1
    This approach works only starting with [Spring 5.1](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/Profiles.html#of-java.lang.String...-) => Spring Boot 2.1 or later – t0r0X May 10 '20 at 03:02
5

With newer versions of Spring, the acceptsProfiles method which accepts strings has been deprecated.

To do the equivalent work as in Cyril's question, you would need to leverage the new method parameter. This newer format also gives you the flexibility to author profile expressions which are more powerful than what was in place prior, thus eliminating the need to negate the entire acceptsProfiles expression itself.

public class NotConsoleAndDevCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        return environment.acceptsProfiles(Profiles.of("!console & !dev"));
    }
}
Makoto
  • 104,088
  • 27
  • 192
  • 230
0

Another solution would be instead of excluding the 2 profiles (console, dev) you can just include all the others

@Profile({"qc", "prod"})
senior_dev
  • 23
  • 1
  • 6