Not loading a Spring bean when a certain profile is set
Asked Answered
C

1

13

Background: So, I've got several beans that interface external systems. For development, it's convenient to mock the external systems and replace the interfacing beans with some implementations that produce more or less static responses. So what I've been doing is to create an interface, the real implementation and a stub implementation like this:

public interface ExternalService {
// ...
}

@Service
public class ExternalServiceImpl implements ExternalService {
// ...
}

@Service
@Primary
@Profile({"stub"})
public class StubExternalService implements ExternalService {
// ...
}

...and this works great: if the stub profile is not present, the stub-bean does not get loaded at all. If it is present, it nicely supersedes the real implementation because of the @Primary annotation.

Problem: Now, however, I've run for the first time in a situation where I've actually got two real implementations of the same interface. One of them is defined as primary, but the other may also be used by loading it from the application context.

I'd still like to create a stub service to replace them both, but this time my old way of defining the stub as @Primary doesn't work, because there's already one primary implementation. Basically what I'd need is a way of not loading the primary bean when the stub profile is set, but I'm at loss on how exactly to do that. Web searches or other Stack Overflow questions don't seem to be helping.

Custodial answered 1/8, 2014 at 9:2 Comment(0)
C
25

Turns out the answer was surprisingly simple: you add a not-operator (!) in front of the profile name:

@Service
@Primary
@Profile({"!stub"})
public class ExternalServiceImpl implements ExternalService {
// ...
}

This way the bean is only loaded when the stub-profile is not active. The support for this feature was added in Spring 3.2 M1.

There's one caveat, though: if you write @Profile({"!stub", "foo"}), the comma is treated as "or", not "and". So this example bean would be activated either if stub was not active or if foo was active.

Edit/Add: Spring 5.1 added a support for a new expression language for profiles: !stub & foo is activated when stub is not active and foo is active. Great success! You can even mix and match ands and ors provided that you use parenthesis: production & (us-east | eu-central).

Custodial answered 4/8, 2014 at 7:59 Comment(1)
Zero is right but there is actually a way to obtain the AND. If you create a specific configuration class and annotate it with @Profile('!dev') and then inside you create a bean with @Profile('!prod') annotation it will work as AND condition, so in this case: if !dev && !prod .Cinquefoil

© 2022 - 2024 — McMap. All rights reserved.