Close

Spring - Resolving ambiguity by using @Qualifier

[Last Updated: Dec 8, 2020]

Specifying an unique name for @Bean annotation is necessary if

  1. the configuration provides more than one implementations for a bean
  2. or if we want to inject bean instances by name rather than by type.

To match a named bean to an injection point (or in other words to qualify a bean to an injection point), the bean's property name (at the injection point) should match with the bean definition name.


Matching bean by using @Qualifier





Or another way to fix above exception is to use @Qualifier at both places:


Example

package com.logicbig.example.service;

public interface OrderService {

  String getOrderDetails(String orderId);
}
package com.logicbig.example.service.impl;

import com.logicbig.example.service.OrderService;

public class OrderServiceImpl1 implements OrderService {

  public String getOrderDetails(String orderId) {
      return "Order details from impl 1, for order id=" + orderId;
  }
}
package com.logicbig.example.service.impl;

import com.logicbig.example.service.OrderService;

public class OrderServiceImpl2 implements OrderService {

  public String getOrderDetails(String orderId) {
      return "Order details from impl 2, for order id=" + orderId;
  }
}

Using @Qualifier at injection point

package com.logicbig.example.client;

import com.logicbig.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import java.util.Arrays;

public class OrderServiceClient {

  @Autowired
  @Qualifier("OrderServiceA")
  private OrderService orderService;

  public void showPendingOrderDetails () {
      for (String orderId : Arrays.asList("100", "200", "300")) {
          System.out.println(orderService.getOrderDetails(orderId));
      }
  }
}

Defining beans' name via @Bean#name

package com.logicbig.example;

import com.logicbig.example.client.OrderServiceClient;
import com.logicbig.example.service.OrderService;
import com.logicbig.example.service.impl.OrderServiceImpl1;
import com.logicbig.example.service.impl.OrderServiceImpl2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppRunner {

  @Bean(name = "OrderServiceA")
  public OrderService orderServiceByProvider1() {
      return new OrderServiceImpl1();
  }

  @Bean(name = "OrderServiceB")
  public OrderService orderServiceByProvider2() {
      return new OrderServiceImpl2();
  }

  @Bean
  public OrderServiceClient createClient() {
      return new OrderServiceClient();
  }

  public static void main(String... strings) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppRunner.class);
      OrderServiceClient bean = context.getBean(OrderServiceClient.class);
      bean.showPendingOrderDetails();
  }
}

Output

Order details from impl 1, for order id=100
Order details from impl 1, for order id=200
Order details from impl 1, for order id=300

Using @Qualifier

package com.logicbig.example;

import com.logicbig.example.client.OrderServiceClient;
import com.logicbig.example.service.OrderService;
import com.logicbig.example.service.impl.OrderServiceImpl1;
import com.logicbig.example.service.impl.OrderServiceImpl2;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppRunner2 {

  @Bean
  @Qualifier("OrderServiceA")
  public OrderService orderServiceByProvider1() {
      return new OrderServiceImpl1();
  }

  @Bean(name = "OrderServiceB")
  public OrderService orderServiceByProvider2() {
      return new OrderServiceImpl2();
  }

  @Bean
  public OrderServiceClient createClient() {
      return new OrderServiceClient();
  }

  public static void main(String... strings) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppRunner2.class);
      OrderServiceClient bean = context.getBean(OrderServiceClient.class);
      bean.showPendingOrderDetails();
  }
}

Output

Order details from impl 1, for order id=100
Order details from impl 1, for order id=200
Order details from impl 1, for order id=300

Example Project

Dependencies and Technologies Used:

  • Spring Context 4.2.3.RELEASE: Spring Context.
  • JDK 1.8
  • Maven 3.6.3

Spring - @Qualifier Example Select All Download
  • spring-inject-bean-by-name
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • client
                • service
                  • impl
                • AppRunner.java

    NoUniqueBeanDefinitionException

    The @Autowired annotation tells Spring where the Injection Point is (i.e. where an injection needs to occur). On finding such injection point, Spring tries to find a configured bean (configured via @Bean) of the same type, if it finds such bean, it injects it to the injection point. If it finds two or more such beans, we will get an NoUniqueBeanDefinitionException. To avoid the exception, we have to use the @Qualifier annotation as we have seen in the above example.


    In above example if we remove name element from both methods annotated with @Bean and remove the @Qualifier annotation from the injection point in OrderServiceClient, we will have following exception:

    org.springframework.beans.factory.NoUniqueBeanDefinitionException:
    No qualifying bean of type  [com.logicbig.example.service.OrderService] is defined:
    expected single matching bean but found 2:
    orderServiceByProvider1,orderServiceByProvider2


    Use of a qualifier shouldn't tie to an implementation specific 'name'

    Some people may argue the usage of a Qualifier. The point could be, why we have to put a qualifier name to the application code (which seems to be tied to a particular implementation)? Well, qualifier should not be tied to implementation details if we use it wisely.

    Let's consider our above example again. Assume the Qualifier with name OrderServiceA is meant to show some extended details about each orders. Whereas, OrderServiceB just shows the order summary. In that sense the application code should be in full control, whether it wants to show extended details or just summary. So the name of the Qualifier should be assigned in a business logic sense but not in implementation sense. There might still be multiple implementations of summary or extended order details available by some other providers which are not wired yet, but can be used anytime we want to. In that case, our qualifier will stay the same without affecting application logic, we just have to wire a different bean per our needs.

    See Also