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.
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)
.