Wednesday, 6 April 2016

Autowiring using annotations in Spring

Though autowiring of the beans can be done using XML configuration for autowiring but Spring goes one step further and provides autowiring using annotations which leads to shorter and more concise configuration.

Broadly Spring provides two ways to annotate beans for autowiring -

  • Using @Autowired annotation - It is a spring specific annotation.
  • Using @Inject annotation - It is provided by JSR-330 (Dependency Injection for Java) annotations contained in the javax.inject package. Spring supports this annotation.

Configuration required for autowiring using annotation

To enable autowiring using annotation, you have to register 'AutowiredAnnotationBeanPostProcessor' class.

  • You can directly provide this class in xml config -
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    
  • You can include the <context:annotation-config/> tag in an XML-based Spring configuration (notice the inclusion of the context namespace in XML). This is the preferred way.
    <?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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
      <context:annotation-config/>
    </beans>
    

By using this tag following post-processors are implicitly registered AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor and RequiredAnnotationBeanPostProcessor.

@Autowired autowiring example

Note that @Autowired annotation can be applied on -

  • setter method
  • constructor
  • field
Here we'll see example for all of these options.

@Autowired annotation on setter

When @Autowired annotation is used on a setter, it is equivalent to autowiring="byType" in autowiring using configuration file.

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.

interface IPayService

public interface IPayService {
 void performPayment();
}

PayServiceImpl class

import org.springframework.beans.factory.annotation.Autowired;
public class PayServiceImpl implements IPayService {
    private IPayment payment;

    public void performPayment() {
        // calling method on Ipayment implementing class
        payment.executePayment();
    }
    
    public IPayment getPayment() {
        return payment;
    }

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

Here note the @Autowired annotation on setPayment() method.

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:aop="http://www.springframework.org/schema/aop"
    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/>  
 
 <bean id="cashPaymentBean" class="org.netjs.prog.CashPayment" />
 
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.PayServiceImpl">
      <!-- <property name="payment" ref="cashPaymentBean" /> -->
  </bean> 
</beans>

Here note the inclusion of <context:annotation-config/> tag 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). It will be injected automatically now because of the @Autowired annotation.

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");
        // Getting PayServiceImpl 
        IPayService bean = (IPayService) context.getBean("paymentBean");
        bean.performPayment();
        context.close();
    }
}

Output

Perform Cash Payment -

@Autowired annotation on field

When @Autowired annotation is used on a property, it is equivalent to autowiring="byType" when autowiring is done using configuration file.

In this case PayServiceImpl will change to have @Autowired annotation on the field. When using @Autowired with field you don't even need the setter method for that field.

PayServiceImpl class

public class PayServiceImpl implements IPayService {
 @Autowired
 private IPayment payment;

 public void performPayment() {
  // calling method on Ipayment implementing class
  payment.executePayment();
 }
 
 public IPayment getPayment() {
  return payment;
 }
 
 /*public void setPayment(IPayment payment) {
  this.payment = payment;
 }*/

}

Rest of the things remain the same as used in the example for @Autowired on setter.

@Autowired annotation on constructor

When @Autowired annotation is used on a bean's constructor, it is equivalent to autowiring="constructor" when autowiring is done using configuration file.

In this case PayServiceImpl will change to have @Autowired annotation on the constructor of the class. When using @Autowired on constructor you don't even need the setter method for that field.

import org.springframework.beans.factory.annotation.Autowired;

public class PayServiceImpl implements IPayService {
 
 private IPayment payment;
 // Constructor 
 @Autowired
 PayServiceImpl(IPayment payment){
  this.payment = payment;
 }

 public void performPayment() {
  // calling method on Ipayment implementing class
  payment.executePayment();
 }
 
 public IPayment getPayment() {
  return payment;
 }
 
 /*public void setPayment(IPayment payment) {
  this.payment = payment;
 }*/
}

Rest of the things remain the same as used in the example for @Autowired on setter.

@Autowired annotation on arbitrary methods

You can also apply the annotation to methods with arbitrary names and/or multiple arguments, Example as taken from Spring reference doc

public class MovieRecommender {
  private MovieCatalog movieCatalog;
  private CustomerPreferenceDao customerPreferenceDao;
  @Autowired
 public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
   this.movieCatalog = movieCatalog;
   this.customerPreferenceDao = customerPreferenceDao;
 }
 // ...
}

@Autowired annotation with required=false

The default behaviour for the annotation is to treat annotated methods, constructors, and fields as indicating required dependencies. Thus the autowiring fails whenever zero candidate beans are available. You can make the autowiring optional to avoid such errors, this can be done by using required="false" with the @Autowired annotation.

@Autowired(required=false)
private IPayment payment;

Conflict resolution using @Qualifier

What if there are more than one bean in the configuration file whose type is compatible with the autowired property. In that case Spring won't be able to decide which bean to use, it will throw an exception instead.

In the above application we had used only one implementation of IPayment, CashPayment so there was no problem in automatically wiring it. Now suppose we have one more implementation of the IPayment interface, Credit Payment. In that case with the current setup you'll get NoUniqueBeanDefinitionException because Spring won't know which Payment class to wire.

CreditPayment class

public class CreditPayment implements IPayment {
 public void executePayment() {
  System.out.println("Performing credit payment ");
 }
}

Adding CreditPayment definition in config

<bean id="creditPaymentBean" class="org.netjs.prog.CreditPayment" />

Now if you run the code you will get NoUniqueBeanDefinitionException -

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentBean': 
Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: 
Could not autowire field: private org.netjs.prog.IPayment org.netjs.prog.PayServiceImpl.payment; 
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type [org.netjs.prog.IPayment] is defined: expected single matching bean but found 2: 
cashPaymentBean,creditPaymentBean
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)

In this kind of scenario, to avoid ambiguity you can use @Qualifier annotation to qualify the bean. Suppose you want to inject cashPaymentBean then you can qualify it by passing bean name with the qualifier annotation.

@Qualifier example

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class PayServiceImpl implements IPayService {
 @Autowired
 @Qualifier("cashPaymentBean")
 private IPayment payment;

 public void performPayment() {
  // calling method on Ipayment implementing class
  payment.executePayment();
 }
 
 public IPayment getPayment() {
  return payment;
 }
 
 /*public void setPayment(IPayment payment) {
  this.payment = payment;
 }*/
}

Using JSR-330 @Inject annotation

Instead of @Autowired, @javax.inject.Inject annotation can be used which is part of the standard JSR-330: Dependency injection for Java. So, if you don't want Spring specific annotations and want to go with standard annotation then use @Inject.

As with @Autowired, it is possible to use @Inject at the class-level, field-level, method-level and constructor-argument level.

Note one thing though. @Inject has no 'required' attribute as provided with @Autowired, so @Inject annotated dependencies can't be optional, an exception will be thrown if they are not fulfilled.

@Named annotation

If you would like to use a qualified name for the dependency that should be injected, instead of @Qualifier you can use @Named annotation which is again part of the standard JSR-330: Dependency injection for Java.

Example with @Inject and @Named

Let's take the same case where we have two payment classes CashPayment and CreditPayment. If you want to make sure that CashPayment bean is the one which is injected then using @Inject and @Named annotations the PayServiceImpl class will look like -

import javax.inject.Inject;
import javax.inject.Named;

public class PayServiceImpl implements IPayService {
 @Inject
 @Named("cashPaymentBean")
 private IPayment payment;

 public void performPayment() {
  // calling method on Ipayment implementing class
  payment.executePayment();
 }
 
 public IPayment getPayment() {
  return payment;
 }
 
 @Inject
 public void setPayment(@Named("cashPaymentBean")IPayment payment) {
  this.payment = payment;
 }
}

Note here that @Inject annotation is used with the setter.

Configuration file for the example

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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/>  
 
 <bean id="cashPaymentBean" class="org.netjs.prog.CashPayment" />
 
 <bean id="creditPaymentBean" class="org.netjs.prog.CreditPayment" />
 
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.PayServiceImpl">
  </bean>
</beans>

Are annotations better than XML for configuring Spring?

According to the Spring reference doc -

The introduction of annotation-based configurations raised the question of whether this approach is 'better' than XML. The short answer is it depends. The long answer is that each approach has its pros and cons, and usually it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control.

That's all for this topic Autowiring using annotations in Spring. 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. What is Dependency Injection in Spring
  3. Spring example program using XML configuration
  4. Spring example program using JavaConfig and Annotations
  5. How to inject prototype scoped bean in singleton bean
  6. Different bean scopes in Spring
  7. Autodiscovery of bean using componenent-scan in Spring

You may also like -

>>>Go to Spring tutorial page

No comments:

Post a Comment