Spring series, part 2: @Qualifier and @Resource

Last time I talked about @Primary and @Autowire as a useful means of dependency injection. Today I will brief you on how @Autowire compares to its more standard counterpart @Resource and demonstrate @Qualifier as a handy complement to @Autowire.


Both @Autowire and @Resource serve the same purpose, i.e. finding the right bean, but work in a different way. 

@Autowire is a framework-specific feature and relies on a data type rather than on a bean identifier. That makes it convenient to use, but presents a risk of run-time exceptions in case there is more than a single suitable candidate for dependency injection. As I showed in my previous post the risk can be decreased by using @Primary, but surely there are pitfalls too.

@Resource in contrast to @Autowire is a standard annotation (JSR-250) and looks up the relevant bean by name. Whereas @Autowire saves some typing, @Resource is very specific and eliminates any confusion about which bean should be injected.

Let's set a "fruitful" example.
 public interface IFruit {  
   String whoAmI();  
 }  
An apple, a banana and a lazy lemon:
 @Service("apple")  
 public class Apple implements IFruit {  
   @Override  
   public String whoAmI() {  
     return "apple";  
   }  
 }  
 @Service("banana")  
 public class Banana implements IFruit {  
   @Override  
   public String whoAmI() {  
     return "banana";  
   }  
 }  
 @Service("lemon")  
 @Lazy  
 public class Lemon implements IFruit {  
   @Override  
   public String whoAmI() {  
     return "lemon";  
   }  
 }  
Now, the test below is bound to fail. That reminds us that @Autowire is indeed datatype-driven. In this case, there are three suitable implementations and Spring can't really tell which one to use:
 import junit.framework.Assert;  
 import org.junit.Test;  
 import org.junit.runner.RunWith;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.test.context.ContextConfiguration;  
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
   
 @RunWith(SpringJUnit4ClassRunner.class)  
 @ContextConfiguration("classpath:spring-config.xml")  
 public class FruitAppTests {  
   @Autowired  
   private IFruit fruit;  
   
   @Test  
   public void anAppleShouldBeAutowired() {  
     Assert.assertEquals("apple", fruit.whoAmI());  
   }  
 }  
A failure comes as no surprise:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type [IFruit] is defined: 
expected single matching bean but found 3: apple,banana,lemon  
However, if we simply rename the the variable from fruit to apple, Spring is smart enough to pick the right bean(!):
 ..   
  @Autowired  
  private IFruit apple;  
   
  @Test  
  public void anAppleShouldBeAutowired() {  
   Assert.assertEquals("apple", apple.whoAmI());  
  }  
 ..  
Convention over configuration:
org.springframework.beans.factory.support.DefaultListableBeanFactory 
- Retrieved dependent beans for bean 'apple': [FruitAppTests]  
For the sake of the exercise let's revert the variable name back to fruit and use @Qualifier to help the framework resolve the bean:
 ..  
 import org.springframework.beans.factory.annotation.Qualifier;  
 ..  
  @Qualifier("apple")  
  @Autowired  
  private IFruit fruit;  
 ..  
The correct bean is once again successfully found and injected. The same can be achieved by using @Resource:
 ..  
 import javax.annotation.Resource;  
 ..  
  @Resource(name = "banana")  
  private IFruit fruit2;  
   
  @Test  
  public void aBananaShouldBeTheResource() {  
   Assert.assertEquals("banana", fruit2.whoAmI());  
  }  
 ..  
Finally, I wanted to touch the topic of a postponed bean creation. In the previous post, things didn't go that well and we witnessed a premature instantiation despite having @Lazy in place. This time though, there is no trace of the lazily loaded bean (Lemon):
org.springframework.beans.factory.support.DefaultListableBeanFactory 
- Creating instance of bean 'apple'  
org.springframework.beans.factory.support.DefaultListableBeanFactory 
- Creating instance of bean 'banana'  
Great, but is the @Lazy actually needed? The 'lemon' bean is never used in our tests anyway. Let's see what happens when the @Lazy is removed:
org.springframework.beans.factory.support.DefaultListableBeanFactory 
- Creating instance of bean 'lemon'  
org.springframework.beans.factory.support.DefaultListableBeanFactory - 
Eagerly caching bean 'lemon' to allow for resolving potential circular 
references  
As you can see skipping @Lazy triggers the bean load, even though the bean is never referenced. I will talk about @Lazy in detail in my next post. Stay tuned.

Source Code

Previous: Part 1 - @Primary Next: Part 3 - @Lazy