@Qualifier Example | Spring Boot

Animal.java

package com.example.demo;

public interface Animal {
    public void makeNoise();
}

Cow.java

package com.example.demo;

import org.springframework.stereotype.Component;

@Component("Cow")
public class Cow implements Animal {
    public void makeNoise() {
        System.out.println("Moo");
    }
}

Pig.java

package com.example.demo;

import org.springframework.stereotype.Component;

@Component("Pig")
public class Pig implements Animal {
    public void makeNoise() {
        System.out.println("Oink");
    }
}

Farm.java

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Farm {

    @Autowired
    @Qualifier("Pig")
    Animal animal;

    public Farm(){
    }
}

DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

@Qualifier tells Spring which type of Animal should be injected as a dependency to the Farm class.

Remember the @Autowired annotation tells Spring to inject an instance of Animal.

Since both Cow and Pig implement Animal, Spring must know which implementation to inject or "autowire".

@Qualifier specifies which implementation to use.

Remember that Spring manages an application context. This application context is a collection of Spring managed beans. When you have two or more beans of the same type, you must "qualify" which bean to use when using @Autowired annotation.

What is @Qualifier in Spring Boot?

@Qualifier is an annotation used to specify which Spring managed bean should be injected via @Autowired.

If you were to run the example without @Qualifier, the following exception would be thrown...

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'farm': Unsatisfied dependency expressed through field 'animal'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.Animal' available: expected single matching bean but found 2: Cow,Pig

This error says it all. Both Cow.java and Pig.java implement the same Animal interface. Both classes are also Spring managed beans via the @Component annotation.

Remember that @Autowired checks the application context for a bean of type Animal to automatically inject as a dependency into the farm class. When it finds two beans with the same type, it doesn't know which one to inject.

This is why @Qualifier is used. It "qualifies" which bean to inject.

Without the @Qualifier annotation, Spring would have no way of knowing which implementation to use.

What is the use of @Qualifier annotation in Spring?

The use of @Qualifier is to support the @Autowired annotation when it needs help.

Typically, @Autowired can implicitly, automatically inject a Spring managed bean by type. In some cases (like the example above), you may have multiple beans implementing the same interface like Animal.

When two or more beans implement the same interface, @Qualifier is used to tell @Autowired what exactly to inject.

Using @Qualifier to identify the bean that should be consumed

Let's go back to our example...

Cow.java

@Component("Cow")
public class Cow implements Animal {
 ...

Pig.java

@Component("Pig")
public class Pig implements Animal {
 ...

Notice how both implementations of animal use the @Component annotation. This tells Spring to register these classes as beans in the application context.

When we use @Qualifier like this...

Farm.java

...
@Autowired
@Qualifier("Pig")
Animal animal;
...

We match the qualifier with the component we want to inject via the component name, in this case "Pig".

Alternate approach...

We could take a slightly different approach to achieve the same result...

Cow.java

@Component
@Qualifier("Cow")
public class Cow implements Animal {
 ...

Pig.java

@Component
@Qualifier("Pig")
public class Pig implements Animal {
 ...

This accomplishes the same result. The @Qualifier annotation in the class files to label the components instead of defining the name on the @Component annotation itself.

@Qualifier vs @Primary

Both @Qualifier and @Primary can help @Autowired specify which bean to inject.

For example, lets comment out the @Qualifier in our Farm class...

@Component
public class Farm {

    @Autowired
//  @Qualifier("Pig")
    Animal animal;

    public Farm(){
    }
}

and add @Primary to our Pig class...

@Component("Pig")
@Primary
public class Pig implements Animal{
    public void makeNoise() {
        System.out.println("Oink");
    }
}

This will run and work the same. This is because @Primary sets a default.

In fact, we can remove ("Pig") altogether because @Primary knows this is the default implementation of Animal to use.

If we keep @Primary on the Pig class but specify a qualifier like this...

@Component
public class Farm {

    @Autowired
//  @Qualifier("Cow")
    Animal animal;

    public Farm(){
    }
}

then the Cow class will still be used when initializing Farm.

This is because @Qualifier takes precedence over @Primary

To summarize...

  • Both @Qualifier and @Primary help @Autowired determine which bean to inject
  • @Primary defines a default implementation to use if multiple beans have the same type
  • @Qualifier takes precedence over @Primary because it is more specific.

Conclusion

Only concern yourself with the @Qualifier annotation when using @Autowired.

Remember that the sole purpose of @Qualifier is to assist @Autowired in deciding which bean to implicitly inject into another bean.

If all of your beans have different types then there is no need to worry about @Qualifier. @Qualifier only matters when multiple managed beans have the same type (like we saw with Cow and Pig both implementing the Animal interface).

@Primary specifies a default and can serve as a substitute for @Qualifier.

@Qualifier takes precedence over @Primary if both are used.

Lastly, you can avoid using @Qualifier altogether when your field names match the class name...

@Component
public class Farm {

    @Autowired
    Animal Pig;

    public Farm(){
    }
}

Note: this is case sensitive and the field name MUST match the class name for this to work without using @Qualifier.

Your thoughts?