June 20, 2022

Spring Autowiring Using @Autowired Annotation

In Spring framework we provide configuration data using which Spring container can instantiate beans and inject dependencies. Autowiring in Spring means Spring container can automatically resolve collaboration among beans (bean dependencies) by inspecting the contents of the ApplicationContext.

Spring Autowiring Modes

There are four autowiring modes in Spring framework.

  • no- By default there is no autowiring when using XML based confirguration. Bean references must be defined by ref elements.
  • byName- In Autowiring by property name, Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean contains a item property (that is, it has a setItem() method), Spring looks for a bean definition named item and uses it to set the property.
  • byType- In autowiring byType, Spring autowires a property if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown. If there are no matching beans, nothing happens (the property is not set).
  • constructor- Autowiring by constructor is similar to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.

Autowiring in Spring

For autowiring in Spring there are three options.

  1. You can choose to autowire using the traditional XML based configuration. See post- Spring Autowiring Example Using XML Configuration for example.
  2. Autowiring using @Autowired annotation.
  3. Autowiting using JSR 330’s @Inject annotation. See post Spring Autowiring Using @Inject and @Named Annotations for example.

In this post we’ll see Spring autowiring example using @Autowired annotation.

Enabling @Autowired annotation

1. You can enable autowiring using @Autowired annotation in Spring by registering 'AutowiredAnnotationBeanPostProcessor' class.

<bean class = "org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

2. By using <context:annotation-config/> element in an XML-based Spring configuration. <context:annotation-config/> element implicitly registers post-processors. The implicitly registered post-processors include AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor and the aforementioned RequiredAnnotationBeanPostProcessor.

3. By using <context:component-scan> element in an XML-based Spring configuration. The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config> element. Most of the time you'll use this element.

4. By using @ComponentScan annotation if you are using Java based Spring configuration. See example in this post- Spring @ComponentScan Annotation

Using @Autowired annotation

  • You can apply the @Autowired annotation to constructors.
  • You can apply the @Autowired annotation to setter methods.
  • You can apply @Autowired to fields.
  • You can also apply the annotation to methods with arbitrary names and multiple arguments.

Spring @Autowired annotation examples

We’ll see examples of @Autowired using all the above options. In the example there is a class to place order called OrderService and purchase can be done from a Store. In OrderService class dependency for store has to be autowired.

Using @Autowired annotation on setter

@Autowired annotation on a setter method is equivalent to autowiring="byType" in autowiring using configuration file.

public interface OrderService {
  public void buyItems();
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowired on Setter
  @Autowired
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}
public interface IStore {
  public void doPurchase();
}
import org.springframework.stereotype.Service;

@Service
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Retail Store");
  }
}
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.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
          
  <context:component-scan base-package="com.knpcode.springproject.service" />
</beans>

You can use the following class with main method to read the configuration and call the bean method.

public class App {
  public static void main( String[] args ){
    // create context using configuration
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
    OrderService order =  context.getBean(OrderServiceImpl.class);
    order.buyItems();
    // close the context
    context.close();
  }
}

Using @Autowired annotation on constructor

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

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowired on constructor
  @Autowired
  public OrderServiceImpl(IStore store){
      this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

If the target bean defines only one constructor, Spring Framework 4.3 onward an @Autowired annotation on such a constructor is no longer necessary. However, if several constructors are available, at least one must be annotated to let the container know which one to use.

Using @Autowired annotation on field

@Autowired annotation on a field is equivalent to autowiring="byType" in autowiring using configuration file.

@Service
public class OrderServiceImpl implements OrderService {
  // Autowiring on a field
  @Autowired
  private IStore store;	
  public void buyItems() {
    store.doPurchase();
  }
}

Using @Autowired annotation on arbitrary methods

You can also apply the annotation to methods with arbitrary names and multiple arguments.

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowiring on a method
  @Autowired
  public void prepare(IStore store) {
    this.store = store;

  }
  public void buyItems() {
    store.doPurchase();
  }
}

required attribute with @Autowired

By default, autowiring fails when no matching candidate beans are available for a given dependency. The default behavior is to treat annotated methods and fields as required dependencies.

For example in the following bean class if the dependency to store can’t be satisfied an exception is thrown.

@Service
public class OrderServiceImpl implements OrderService {
  @Autowired
  private IStore store;
  ..
  ..
}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': 
Unsatisfied dependency expressed through field 'store'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'com.knpcode.springproject.service.IStore' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

To change this default behavior you can use required = false along with @Autowired annotation. The property is ignored if it cannot be autowired.

@Autowired(required = false)
private IStore store;

As of Spring Framework 5.0, you can also use a @Nullable annotation to indicate that the property can be ignored if it cannot be autowired.

@Autowired
public void setStore(@Nullable IStore store) {
  this.store = store;
}

Conflict resolution using @Primary with Annotation-based Autowiring

When autowiring by type there may be multiple candidates of the same type in such scenario Spring container won't be able to decide which bean to autowire and throw NoUniqueBeanDefinitionException.

For example if there two stores RetailStore and OnlineStore of type IStore.

@Service
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}

Then our example will fail as it won’t be able to determine which store to autowire.

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': 
Unsatisfied dependency expressed through method 'setStore' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type 'com.knpcode.springproject.service.IStore' available: expected single matching bean but found 2: onlineStore,retailStore

Using Spring’s @Primary annotation you can have more control over the selection process. @Primary indicates that a particular bean should be given preference when multiple beans are candidates to be autowired to a single-valued dependency.

By annotating OnlineStore bean with @Primary annotation you can ensure that it is given preference.

@Service
@Primary
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}

Conflict resolution using @Qualifier with Annotation-based Autowiring

Spring’s @Qualifier annotation gives more control over the selection process. You can associate qualifier values with specific arguments, narrowing the set of type matches so that a specific bean is chosen for each argument.

@Service
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Online Store");
  }
}
@Service
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase from Retail Store");
  }
}

There are two beans of same type Istore, by using @Qualifier annotation you can qualify the bean to be autowired.

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  @Autowired
  @Qualifier("retailStore")
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}
Output
16:27:57.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'onlineStore'
16:27:57.981 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
16:27:58.108 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'retailStore'
Doing purchase from Retail Store

That's all for the topic Spring Autowiring Using @Autowired Annotation. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment