Spring @Qualifier Annotation with Example
Spring is one of the most popular Java EE frameworks. It is an open-source lightweight framework that allows Java EE 7 developers to build simple, reliable, and scalable enterprise applications. Spring focuses on providing various ways to manage business objects, making web application development easier compared to classic Java frameworks and APIs like JDBC, JSP, and Java Servlet.
Spring uses advanced techniques such as Aspect-Oriented Programming (AOP), Plain Old Java Object (POJO), and Dependency Injection (DI) to develop enterprise applications. Among these, annotations play a crucial role in simplifying configuration and dependency management. In this article, we are going to understand the Spring @Qualifier Annotation with an example.
Note: We first need to understand the Spring @Autowired Annotation before jumping into the @Qualifier Annotation.
What is @Qualifier Annotation?
The @Qualifier annotation is used to resolve ambiguity when multiple beans of the same type are available for dependency injection. It helps specify which exact bean should be injected into a class when Spring cannot automatically determine the correct one.
Key features of @Qualifier Annotation:
- @Qualifier annotation is always used with @Autowired to resolve ambiguity in dependency injection.
- The value passed to @Qualifier must match the bean ID defined in the Spring configuration.
- @Qualifier can be used on Setter methods, Fields, and Constructor parameters.
Why is @Qualifier Needed?
In Spring, the @Autowired annotation automatically injects dependencies. By default, Spring resolves dependencies using the byType mechanism, meaning it looks for a bean of the same type as the dependency.
But, if multiple beans of the same type exist, Spring cannot determine which one to use, leading to ambiguity. This results in a NoUniqueBeanDefinitionException. For example, consider two beans of type Heart (e.g., humanHeart and octopusHeart). If we try to inject a Heart object into a Human class, Spring will not know which bean to use.
This is where the @Qualifier annotation helps. It allows us to explicitly specify which bean should be injected, resolving the ambiguity.
Working of @Qualifier Annotation
The @Qualifier annotation is used with @Autowired to specify the exact bean to be injected. We provide the bean name (or ID) as a parameter to the @Qualifier annotation.
Example: This example demonstrates how to use @Autowired with @Qualifier to inject a specific bean when multiple beans of the same type exist.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Human {
private Heart heart;
@Autowired
@Qualifier("humanHeart") // Specifies which bean to inject
public void setHeart(Heart heart) {
this.heart = heart;
}
public void startPumping() {
heart.pump();
}
}
Problem with @Autowired Annotation
To understand the need for the @Qualifier annotation, let’s first look at a scenario where the @Autowired annotation can cause issues.
Consider a Human class that depends on a Heart class. The Heart class has a simple method called pump()
1. Heart.java
public class Heart {
public void pump() {
System.out.println("Heart is Pumping");
}
}
2. Human.java
Human class does has a dependency on another class named Heart.
import org.springframework.beans.factory.annotation.Autowired;
public class Human {
private Heart heart;
@Autowired
public void setHeart(Heart heart) {
this.heart = heart;
}
public void startPumping() {
heart.pump();
}
}
Now we want to inject the object of the Heart class inside the Human class by using the @Autowired annotation. So for this, we can write the code something like this
// Java Program to Illustrate Injection of object of the
// Heart class inside the Human class using the @Autowired
// annotation
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
// Class
public class Human {
private Heart heart;
// Annotation
@Autowired public void setHeart(Heart heart)
{
this.heart = heart;
}
// Method
// Calling method of Heart class
public void startPumping() { heart.pump(); }
}
3. beans.xml Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="heartObjValue" class="Heart"></bean>
<bean id="humanObject" class="Human"></bean>
</beans>
4. Main.java
// Java Program to Illustrate Application Class
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Human human = context.getBean("humanObject", Human.class);
human.startPumping();
}
}
Output:
Heart is Pumping
Explanation: In the above code, the @Autowired annotation is used to autowire the Heart object into the Human class by using the Spring Autowiring ‘byType’. When the byType mechanism finds a suitable match, it injects the Heart object without issues.
Note: How @Autowired Work?
- It first tries to resolve dependencies by type.
- If multiple beans of the same type are found, it tries to resolve by name (matching the bean name to the property name).
So in our above example, the "byType" Spring Autowiring mechanism happens and the heart object is injected into the Human class. But where does the problem lie?
The Problem: Multiple Beans of the Same Types
Now inside the beans.xml file let's create another bean of the Heart class.
1. Modified bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="humanHeart" class="Heart"></bean>
<bean id="octopusHeart" class="Heart"></bean>
<bean id="humanObject" class="Human"></bean>
</beans>
Here, we have two beans of the same class: one is "humanHeart" and another one is "octpusHeart". In this scenario, the "byType" autowiring will fail because there are two beans of the same type (Heart). Spring will then attempt to resolve by name, but since the property name "heart" doesn't match either bean id, this also fails.
When we run your application, you'll get the following exception:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'humanObject': Unsatisfied dependency expressed through method 'setHeart' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'Heart' available: expected single matching bean but found 2: humanHeart,octpusHeart at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:720) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'Heart' available: expected single matching bean but found 2: humanHeart,octpusHeart at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
Solution: Using @Qualifier Annotation
This is where the @Qualifier Annotation comes into the picture. When "byType" autowiring fails due to multiple candidates, we can use the @Qualifier Annotation to specify exactly which bean should be wired. For example, in the case of the Human class, we need the "humanHeart" bean, so we can specify that bean using the @Qualifier Annotation. Here's how we can resolve this error:
Modified Human.java
// Java Program to Illustrate Modified Human Class
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
// Class
public class Human {
private Heart heart;
// Annotation
@Autowired
@Qualifier("humanHeart")
// Setter
public void setHeart(Heart heart)
{
this.heart = heart;
}
// Method
public void startPumping() { heart.pump(); }
}
Output:
Heart is Pumping
Alternative: Using @Qualifier on Field Injection
We can also use the @Qualifier annotation directly on the field, eliminating the need for a setter method.
Example: This example demonstrates how to use @Autowired and @Qualifier to inject a specific bean (humanHeart) into a field.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Human {
@Autowired
@Qualifier("humanHeart")
private Heart heart;
public void startPumping() {
heart.pump();
}
}