Reputation: 2702
Given two (or more) implementations of a particular service API, what's the best way to pick which one to use at runtime in my app based on an application property?
Example API:
public interface Greeting {
String sayHello(String username);
}
Implementations:
public class FriendlyGreeting implements Greeting {
public String sayHello(String username) {
return "Hello, " + username;
}
}
public class HostileGreeting implements Greeting {
public String sayHello(String username) {
return "Go away, " + username;
}
}
I've got a separate service class with an @Autowired
constructor that takes an instance of Greeting
. What I want, is based upon a configuration property, to decide which greeting implementation gets injected and used. I came up with using a configuration class to make that decision:
@Configuration
public class GreetingConfiguration {
private String selection;
@Autowired
public GreetingConfiguration(@Value("${greeting.type}") String type) {
this.selection = type;
}
@Bean
public Greeting provideGreeting() {
if ("friendly".equals(selection)) {
return new FriendlyGreeting();
} else {
return new HostileGreeting();
}
}
}
Is this the right way to do what I want? I went down the road of using @Qualifier
on the implementations, and ended up with a mess where Spring saw 3 instances of my Greeting
API, and I needed a configuration anyway to pick which implementation to use and return it with a unique qualifier name on it, and that feels worse than what I settled on.
Upvotes: 6
Views: 7978
Reputation: 1225
Here is a full solution using ideas mentioned by David and Vitor above with @Profile and @Qualifer annotations.
Two beans with same name but Only one is activated based on which profile is defined.
@Profile("profile1")
@Bean("greeting")
public class FriendlyGreeting implements Greeting {
---
@Profile("profile2")
@Bean("greeting")
public class HostileGreeting implements Greeting {
---
@Configuration
public class GreetingConfiguration {
private Greeting greeting;
@Autowired
public GreetingConfiguration(@Qualifier("greeting") Greeting greeting) {
this.greeting = greeting;
}
}
Notes:
GreetingConfiguration
and stick the "greeting" bean wherever you needUpvotes: 2
Reputation: 147
You can use @Conditional
annotations described at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html and https://reflectoring.io/spring-boot-conditionals/
@Profile
annotations mentioned above are based upon @Conditional
(from Spring Framework); see also Spring Boot: org.springframework.boot.autoconfigure.condition
Upvotes: 2
Reputation: 2702
Answering my own question.
@Compass and @user268396 were correct - using Profiles got this working as expected.
I created both implementations, annotated with @Service and @Profile("friendly") or @Profile("hostile"), and could change the property spring.profiles.active
to dev,friendly
for example, and get what I wanted.
Upvotes: 2
Reputation: 365
You can mark both Greeting as @Service and select the chosen one with @Qualifier("yourServiceHere") like this:
@Autowired
@Qualifier("friendlyGreeting")
private Greeting greeting;
Another way you can do it is with profile. You can mark your FriendlyGreeting service with @Service and @Profile("friendly") and the HostileGreeting service with @Service and @Profile("hostileGreeting") and just put in the application.properties the following:
spring.profiles.active=friendly
Upvotes: 2