5

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?

5
  • does it work without the qualifier? Commented Aug 6, 2019 at 12:21
  • Could you please describe your use-case? Qualifiers are used when there are more than one bean of the same type co-exist in Application context and you want to inject a particular bean. In this case no need for dynamic configuration usually, you chose among a set of predefined values for injecting the bean with qualifier Commented Aug 6, 2019 at 12:24
  • @JoseMartinez Without it, i will have to add constructor dependencies manually, which is not nice in a spring project Commented Aug 6, 2019 at 12:26
  • @MarkBramnik 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. Commented Aug 6, 2019 at 12:27
  • Possible duplicate of How to read Qualifier from property file in spring boot? Commented Aug 7, 2019 at 7:54

5 Answers 5

15

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.

Sign up to request clarification or add additional context in comments.

Comments

4

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.

Comments

1

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)

Comments

1

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:

  1. First, you qualify your implementations

@Component("Foo_A")
public class Foo_A implements Foo {
    @Override
    doSomething() {...}
}

@Component("Foo_B")
public class Foo_B implements Foo {
    @Override
    doSomething() {...}
}

  1. Then, wherever you gonna use this (class Bar, for example), you can just use the @Qualifier to specify the implementations you are instantiating and get the value from the property with the @Value. Then, inside the method, with a simple if/else statement, you use the property value to decide which implementation you're going to call.
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();
        }
    }

}

Comments

0

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
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.