Thursday, August 1, 2013

Spring - Autowiring multiple beans of the same type and @Primary annotation

Consider a simple Spring annotation based context, with two beans with @Service annotation, but of the same type:

@Service 
public class CustomerServiceImpl1 implements CustomerService{
 @Override
 public Customer getCustomer(long id) {
  return new Customer(1, "Test1");
 }
}

and

@Service 
public class CustomerServiceImpl2 implements CustomerService{
 @Override
 public Customer getCustomer(long id) {
  return new Customer(1, "Test1");
 }
}

Now, a client which injects in the CustomerService bean as a dependency will fail, as by default the injection of autowired fields is by type and there are two beans in the context of the same type. An error message along these lines is typically what is seen at startup:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [pkg.CustomerService] is defined: expected single matching bean but found 2: customerServiceImpl2,customerServiceImpl1

A standard fix for this is to inject by bean name instead of by type, this way:

public class ConfigTest
{
    @Autowired
    @Qualifier("customerServiceImpl1")
    private CustomerService customerService;
    ...
}    

Note that I have made use of the bean naming convention for stereotype annotations which is described in more detail here

However there is another way to disambiguate which specific bean to inject in, and this is with the @Primary annotation. This annotation indicates that the target bean should be given precedence when autowiring by type. This begs the question, what happens if more than one bean of the same type is annotated with @Primary, well Spring will raise an exception like before. @Primary is expected to be applied on only bean of a specific type.

@Primary is also supported in Java Configuration, so @Bean methods can also be tagged with @Primary to indicate the higher precedence.

@Configuration
public class TestConfiguration
{
 @Bean
 @Primary
 public CustomerService customerService1() {
  return new CustomerServiceImpl1();
 }
 
 @Bean
 public CustomerService customerService2() {
  return new CustomerServiceImpl2();
 } 
}

5 comments:

  1. Than you for tutorial it was helpfully for me!
    Best Regards

    ReplyDelete
  2. Thank you for your post it was helpful for me!!
    Best Regards,

    ReplyDelete
  3. Hi,
    There is also the javax.annotation.Priority annotation that can be used to select the right bean specifically when more than two beans implement the same interface.
    https://javaee-spec.java.net/nonav/javadocs/javax/annotation/Priority.html

    ReplyDelete
  4. What to do if I want to get bean of the class which is not marked Primary?

    ReplyDelete