Reputation: 2013
I have two implementations for one interface, i want to choose what implementation to use based on a configuration. The qualifier solution did not work as it is initialized prior to the configuration. How can I achieve this?
Upvotes: 5
Views: 9552
Reputation: 135
Let's suppose you have an interface:
public interface Foo {
void doSomething();
}
And 2 implementations:
public class Foo_A implements Foo {
@Override
doSomething() {...}
}
public class Foo_B implements Foo {
@Override
doSomething() {...}
}
Now you want to use Foo_A/Foo_B depending on a property value in your properties file:
foo_name: "A"
The simplest way I found to do this:
@Component("Foo_A")
public class Foo_A implements Foo {
@Override
doSomething() {...}
}
@Component("Foo_B")
public class Foo_B implements Foo {
@Override
doSomething() {...}
}
public class Bar {
@Value("${foo_name}")
private String fooName;
@Qualifier("Foo_A")
private Foo fooA;
@Qualifier("Foo_B")
private Foo fooB;
public void doSomething() {
if (fooName.equals("A")) {
fooA.doSomething();
} else {
fooB.doSomething();
}
}
}
Upvotes: 1
Reputation: 42531
I've got your comment:
I have two different implementations of a job and it is possible to change the type every month so if configurable, less deployments and code changes are made.
You might have something like this:
interface Job {
void foo();
}
class JobA implements Job {
void foo() {...}
}
class JobB implements Job {
void foo() {...}
}
class JobExecutor {
Job job;
// autowired constructor
public JobExecutor(Job job) {this.job = job;}
}
And, if I got you right, it doesn't make sense to load two beans simultaneously in the same application context.
But if so, then @Qualifier
is not a right tool for the job.
I suggest using conditions that are integrated into spring boot instead:
@Configuration
public class MyConfiguration {
@ConditionalOnProperty(name = "job.name", havingValue = "jobA")
@Bean
public Job jobA() {
return new JobA();
}
@ConditionalOnProperty(name = "job.name", havingValue = "jobB")
@Bean
public Job jobB() {
return new JobB();
}
@Bean
public JobExecutor jobExecutor(Job job) {
return new JobExecutor(job);
}
}
Now in application.properties
(or yaml whatever you have) define:
job.name = jobA # or jobB
Of course, instead of jobA/jobB
you might use more self-explanatory names from your business domain.
Upvotes: 15
Reputation: 21
knowing that IOC works fine throught constructor maybe I would do something like this:
@Component
public class JobExecutor implements WhateverService {
private final Job myJob;
public JobExecutor(Map<String, Job> allJobImpls,
@Value("${myClass.type}") final String classType) {
this.myJob = allImpls.get(classType);
}
public X myAction() {
// some code
return myJob.whatever(); //for example
}
}
Upvotes: 0
Reputation: 2013
I ended up adding to the main app class the two implementations autowired then define a bean for each:
@Autowired
TypeOneImpl typeOneImpl
@Bean(name = "typeOneImpl")
public InterfaceRClass getTypeOneImpl()
{
return typeOneImpl;
}
Then in the other class I defined a config field
@Value("${myClass.type}")
private String configClassType;
// the below should be defined in constructor
private final InterfaceRClass interfaceRClassElement ;
And added a setter for it with @Autowired annotation:
@Autowired
public void setMyClassType(ApplicationContext context) {
interfaceRClassElement = (InterfaceRClass) context.getBean(configClassType);
}
In configuration, the value should be typeOneImpl (typeTwoImpl is added for an additional implementation)
Upvotes: 1
Reputation: 26552
You could pull it off with if you fiddle around with Spring java-based config a bit, where you programmatically decide the right implementation based on a config value:
@Configuration
public class MyAppContext implements EnvironmentAware{
private Environment env;
@Override
public void setEnvironment(final Environment env) {
this.env = env;
}
@Bean
public MyBeanByConfig myBeanByConfig(){
String configValue = env.getProperty("mybean.config");
if(configValue.equals("1")){
return new MyBeanByConfigOne();
}else{
return new MyBeanByConfigTwo();
}
}
}
and on the qualifier you would put:
@Qualifier("myBeanByConfig")
you may need to add @ComponentScan
and @PropertySource
on the configuration class also.
Upvotes: 4