Background
[wp_ad_camp_1]
This article demonstrates how to qualify beans of the same reference type in Spring. @Qualifier enables you to select and use a particular bean from a set of beans of the same type existing in the Spring container.
Software Environment
- Windows 7 Professional SP1
- Eclipse – Kepler Release
- Java 1.7 (1.7.0_67 – Windows x86)
- Spring Framework
- Spring Context 4.1.2
Create a Maven Project
If you have not yet created a maven project, you may want to follow the steps on How to create a Maven project in Eclipse. Put the following dependency in your pom.xml file:
1 2 3 4 5 6 7 | <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.2.RELEASE</version> </dependency> </dependencies> |
The Codes
All in all, there are only three (3) source code files for this article.
The main class – Application.java
Here, two (2) instances of anynomous class of type JdbcDao is created. They are declared as beans within the Spring container and each is labeled with a name. Bean “mockdao” is a mock-up of the real DAO; while bean “dao” is the real DAO that retrieves data directly from the database.
[wp_ad_camp_2]
Just imagine we are doing a test-driven development where we are at the initial stage of a new development project and persistence storage is still unavailable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package com.turreta.sample; import java.util.HashMap; import java.util.Map; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; @Configuration @ComponentScan public class Application { @Bean(name="mockdao") public JdbcDao mockDao() { // Use this for now while DB is down or still being designed return new JdbcDao() { public Map<String, String> getCountries() { Map<String, String> countries = new HashMap<String, String>(); // Mock data countries.put("PH", "Philippines"); countries.put("SG", "Singapore"); countries.put("TH", "Thailand"); countries.put("MY", "Malaysia"); return countries; } }; } @Bean(name="dao") public JdbcDao dao() { return new JdbcDao() { public Map<String, String> getCountries() { Map<String, String> countries = new HashMap<String, String>(); // Fetch from database return countries; } }; } public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); CommonService service = context.getBean(CommonService.class); for (Map.Entry<String, String> entry : service.getCountries().entrySet()) { System.out.println("Country Code : " + entry.getKey() + " | Country Name : " + entry.getValue()); } } } |
The Interface – JdbcDao.java
1 2 3 4 5 6 7 | package com.turreta.sample; import java.util.Map; public interface JdbcDao { // Country Code -> Country Name Map<String, String> getCountries(); } |
The Service – CommonService.java
This is where you specify which instance of JdbcDao will be used. Bean “mockdao” or “dao”? The codes below uuse “mockdao”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.turreta.sample; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class CommonService { @Autowired @Qualifier("mockdao") // TODO: change this later to use "dao" once database is available private JdbcDao service; public Map<String, String> getCountries() { return this.service.getCountries(); } } |
Sample Output
[wp_ad_camp_3]
1 2 3 4 5 6 | Nov 23, 2014 5:53:02 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@131c89c: startup date [Sun Nov 23 17:53:02 SGT 2014]; root of context hierarchy Country Code : PH | Country Name : Philippines Country Code : MY | Country Name : Malaysia Country Code : SG | Country Name : Singapore Country Code : TH | Country Name : Thailand |
What happens if @Qualifier is not used?
If you comment out line 11 in CommonService.java and run Application.java again, the following exception is thrown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | Nov 23, 2014 5:47:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1697b67: startup date [Sun Nov 23 17:47:57 SGT 2014]; root of context hierarchy Nov 23, 2014 5:47:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext refresh WARNING: Exception encountered during context initialization - cancelling refresh attempt org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commonService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.turreta.sample.JdbcDao com.turreta.sample.CommonService.service; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84) at com.turreta.sample.Application.main(Application.java:47) Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.turreta.sample.JdbcDao com.turreta.sample.CommonService.service; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:555) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 12 more Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1061) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:527) ... 14 more Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commonService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.turreta.sample.JdbcDao com.turreta.sample.CommonService.service; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84) at com.turreta.sample.Application.main(Application.java:47) Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.turreta.sample.JdbcDao com.turreta.sample.CommonService.service; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:555) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ... 12 more Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.turreta.sample.JdbcDao] is defined: expected single matching bean but found 2: dao,mockdao at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1061) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:527) ... 14 more |