Monday, 25 April 2016

@Resource annotation in Spring autowiring

Apart from supporting JSR-330 annotations like @Inject and @Named for autowiring, Spring also supports injection using the JSR-250 @Resource annotation on fields or bean property setter methods.

@Resource takes a name attribute, and by default Spring interprets that value as the bean name to be injected. In other words, it follows by-name semantics. That's where it differs from other annotations for autowiring like @Autowired and @Inject which are equivalent to autowiring="byType" in autowiring using XML configuration file.

In case no name is provided with @Resource annotation explicitly, the default name is derived from the field name or setter method.

  • In case of a field, it takes the field name;
  • In case of a setter method, it takes the bean property name.

As example, if we take the following class, then bean with name "payment" will be injected into its setter method

public class PayServiceImpl {

 private IPayCash payment;

 @Resource
 public void setPayment(IPayCash payment) {
  this.payment = payment;
 }
}

But there is also default switching with @Resource annotation, if it doesn't find the bean with the same name it will try to match using type. So if you see how it compares with the other two annotations @Autowired and @Inject -

@Autowired and @Inject

  1. Matches by Type
  2. Restricts by Qualifiers
  3. Matches by Name

@Resource

  1. Matches by Name
  2. Matches by Type
  3. Restricts by Qualifiers (ignored if match is found by name)

So you can see in case of @Autowired and @Inject switch to "byType" happens only after restricting by Qualifier. Whereas with @Resource it switches to byType if it doesn't find the bean by name.

Another difference between these two types of annotation is that @Autowired and @Inject annotations use the 'AutowiredAnnotationBeanPostProcessor' to inject dependencies. Whereas '@Resource' annotation uses the 'CommonAnnotationBeanPostProcessor' to inject dependencies.

Source : http://stackoverflow.com/questions/4093504/resource-vs-autowired

Example code

Here we have a class PayServiceImpl which has a field payment of type IPayment which we have to autowire. Also class CashPayment which implements IPayment interface.

PayServiceImpl class

import javax.annotation.Resource;

public class PayServiceImpl {
 
 @Resource(name="cashPaymentBean")
 private IPayment payment;
 
 public void performPayment() {
  payment.executePayment();
 }
 
 
 public IPayment getPayment() {
  return payment;
 }

 
 public void setPayment(IPayment payment) {
  this.payment = payment;
 }
}

Here note the @Resource annotation with name attribute on the field payment.

Interface IPayment

public interface IPayment {
 void executePayment();
}

CashPayment class

public class CashPayment implements IPayment{
 public void executePayment() {
  System.out.println("Perform Cash Payment "); 
 }
}

XML configuration file

<?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 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
  
  <context:annotation-config />    
 
 <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.exp.Spring_Example.CashPayment" />
 
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="payServiceBean" class="org.netjs.exp.Spring_Example.PayServiceImpl" >
  <!--     <property name="payment" ref="cashPaymentBean" /> -->
  </bean>
</beans>

Here note that the CashPayment class bean is defined with the same name "cashPaymentBean" as used in the @Resource annotation. <context:annotation-config /> tag is added which is required for autowired annotation. Also note that ref for cashPaymentBean is no longer required as a property in paymentBean (it is commented in the config).

You can use the following code to run this program -

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App 
{
    public static void main( String[] args ){
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
     PayServiceImpl bean = (PayServiceImpl) context.getBean("payServiceBean");
     bean.performPayment();
        context.close();
    }
}

Checking switching to byType

As already mentioned @Resource annotation will switch to "byType" if it is not able to match the bean by name. You can test it by removing the "name" attribute from @Resource annotation.

@Resource
private IPayment payment;

In this case autowiring will be tried by looking for the bean named "payment". Since it is not there so it will match by type and the program will still run.

BeanNotOfRequiredTypeException

Since @Resource annotation tries to match by Name and then by type that may result in exception in some scenarios. Let's see an example. Here we have one more interface IPayCash and field payment in class PayServiceImpl is of type IPayCash.

IPayCash interface

public interface IPayCash {
 void executePayment();
}

PayServiceImpl class

import javax.annotation.Resource;

public class PayServiceImpl {
 
 @Resource(name="cashPaymentBean")
 private IPayCash payment;
 
 
 public void performPayment() {
  payment.executePayment();
 }
 
 
 public IPayCash getPayment() {
  return payment;
 }

 
 public void setPayment(IPayCash payment) {
  this.payment = payment;
 }
}

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 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
  
  <context:annotation-config />    
 
 <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.exp.Spring_Example.CashPayment" />
 
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="payServiceBean" class="org.netjs.exp.Spring_Example.PayServiceImpl" >
  <!--     <property name="payment" ref="cashPaymentBean" /> -->
  </bean>
</beans>

If you see here in XML configuration CashPayment bean is defined with id "cashPaymentBean" and in Class PayServiceImpl @Resource annotation also has the same name attribute @Resource(name="cashPaymentBean") so name matching will be there and no problem.

If you have noticed types are different, in XML configuration CashPayment class is defined which is of type IPayment, whereas in PayServiceImpl payment field is of type IPayCash. Thus at run time because of type mismatch "BeanNotOfRequiredTypeException" will be thrown.

Output

WARNING: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'payServiceBean': 
Injection of resource dependencies failed; nested exception is 
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'cashPaymentBean' 
must be of type [org.netjs.exp.Spring_Example.IPayCash], but was actually of type 
[org.netjs.exp.Spring_Example.CashPayment]
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:311)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at org.netjs.exp.Spring_Example.App.main(App.java:10)


That's all for this topic @Resource annotation in Spring autowiring. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Autowiring using XML configuration in Spring
  2. Autowiring using annotations in Spring
  3. Autodiscovery of bean using componenent-scan in Spring
  4. Constructor-based dependency injection in Spring
  5. Spring example program using automatic configuration

You may also like -

>>>Go to Spring tutorial page

No comments:

Post a Comment