1. Introduction

Spring lets us define different implementations of an interface as beans. But when we want to wire these beans into the target bean, we should specify which implementation we want to use. Otherwise, Spring fails to inject the dependency.

In this tutorial, we'll look at how we can disambiguate Spring beans.

2. @Primary Annotation

Firstly, we'll look at the @Primary annotation to disambiguate Spring beans.

When we have multiple implementations of an interface, we can define one of the implementations as primary using @Primary:

public interface CalculationService {

    void calculate();
}

Here, we have the CalculationService interface which has several implementations:

@Component
public class SimpleCalculationService implements CalculationService {

    @Override
    public void calculate() {
        System.out.println("Simple calculation");
    }
}

Firstly we have the SimpleCalculationService class implementing CalculationService. Note that we're only using the @Component annotation.

@Component
@Primary
public class AdvancedCalculationService implements CalculationService {

    @Override
    public void calculate() {
        System.out.println("Advanced calculation");
    }
}

Secondly, we have the AdvancedCalculationService class annotated with @Component. Additionally, we're annotating it with @Primary.

As a result, Spring will use AdvancedCalculationService as the primary bean when injecting CalculationService dependencies:

@Component
public class WithoutQualifierArithmeticOperations {

    private final CalculationService calculationService;

    @Autowired
    public WithoutQualifierArithmeticOperations(CalculationService calculationService) {
        this.calculationService = calculationService;
    }
}

Here, the injected CalculationService bean is an instance of AdvancedCalculationService.

3. @Qualifier Annotation

Now, let's look at another way to disambiguate the beans using the @Qualifier annotation.

The @Qualifier annotation lets us define a bean name so that Spring auto-wires the bean having that name.

Firstly we should give a name to our bean:

@Component("simple")
public class SimpleCalculationService implements CalculationService {

    @Override
    public void calculate() {
        System.out.println("Simple calculation");
    }
}

Here, we're specifying the bean name using the @Component annotation. Although Spring generates a default name for every bean, it is generally useful to give an explicit name.

Let's look at the target bean:

@Component
public class WithQualifierArithmeticOperations {

    private final CalculationService calculationService;

    @Autowired
    public WithQualifierArithmeticOperations(@Qualifier("simple") CalculationService calculationService) {
        this.calculationService = calculationService;
    }
}

Here, we're using the @Qualifier annotation to specify that we want to wire the simple CalculationService bean.

4. Generic Type

Next, we'll see how we can use generics to disambiguate Spring beans.

If we define an interface using a generic type parameter, we can distinguish the implementing beans using the actual type argument:

public interface NumberService<T extends Number> {

    T get();
}

Here we have the NumberService class which expects a type argument.

Let's look at the implementations:

public class IntegerNumberService implements NumberService<Integer> {

    // Methods
}

Firstly, we have the IntegerNumberService with the type argument as Integer.

@Component
public class LongNumberService implements NumberService<Long> {

    // Methods
}

Secondly, we have the LongNumberService class with the type argument as Long.

When we want to wire a NumberService implementation into a target bean, we should just specify the NumberService interface with the desired type argument:

@Component
public class WithGenericsArithmeticOperations {

    private final NumberService<Long> numberService;

    @Autowired
    public WithGenericsArithmeticOperations(NumberService<Long> numberService) {
        this.numberService = numberService;
    }
}

5. Summary

In this tutorial, we've investigated how to disambiguate Spring beans using @Primary, @Qualifier and generic type information.

As always, the source code for the examples is available on Github.