June 20, 2022

Spring Autowiring Using @Inject and @Named Annotations

In Spring Autowiring Using @Autowired Annotation we have already seen how container can automatically resolve collaboration among beans (bean dependencies) by annotating the dependencies with @Autowired annotation. As an alternative you can use @Inject annotation for autowiring in Spring.

@Inject and @Named annotations in Spring

Spring 3.0 added support for JSR-330 (Dependency Injection for Java) annotations contained in the javax.inject package such as @Inject and @Named.

  • @Inject is used for autorwiring, it gives you a chance to use standard annotations rather than Spring specific annotation like @Autowired.
  • @Named annotation is used for conflict resolution if there are multiple candidates of the same type.

To use these annotations javax.inject library is needed, Maven dependency for the same is as follows.

<dependency>
  <groupId>javax.inject</groupId>
  <artifactId>javax.inject</artifactId>
  <version>1</version>
</dependency>

Using @Inject annotation

  • You can apply the @Inject annotation to constructors.
  • You can apply the @Inject annotation to setter methods.
  • You can apply @Inject to fields.

We’ll see examples of @Inject annotation using all the above options.

Using @Inject annotation on setter

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

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.

public interface OrderService {
  public void buyItems();
}
import javax.inject.Inject;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowiring on Setter
  @Inject
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

In the class Autowiring on the setter method is done using @Inject annotation.

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");
  }
}
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>

Since JSR-330 standard annotations are scanned in the same way as the Spring annotations so component-scan is able to scan @Inject and @Named annotations too.

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();
  }
}
Output
17:34:09.769 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
17:34:09.806 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'retailStore'
Doing purchase from Retail Store

Using @Inject annotation on constructor

@Inject 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;
  // Autowiring on constructor
  @Inject
  public OrderServiceImpl(IStore store){
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

Using @Inject annotation on field

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

@Service
public class OrderServiceImpl implements OrderService {
  // Autowiring on a field
  @Inject
  private IStore store;

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

@Inject annotation with java.util.Optional or @Nullable

@Inject can also be used with java.util.Optional or @Nullable. Since @Inject does not have a required attribute so the scenario where dependency can not be satisfied under some circumstances has to be taken care of by using either Optional or @Nullable otherwise UnsatisfiedDependencyException will be thrown at the time of bean creation itself.

For example using @Nullable annotation to declare that annotated elements can be null under some circumstance.

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowiring on Setter
  @Inject
  public void setStore(@Nullable IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

By using @Nullable bean initialization will be done but later Null pointer excpetion is thrown if the required dependency is not found.

Using Optional to declare that annotated elements can be null under some circumstance.

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  // Autowiring on Setter
  @Inject
  public void setStore(Optional<IStore> store) {
    if(store.isPresent())
      this.store = store.get();
  }
  public void buyItems() {
    store.doPurchase();
  }
}

Conflict resolution using @Named with @Inject

Using @Named annotation you can use a qualified name for the dependency that should be injected.

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 @Named annotation for conflict resolution in such scenario by qualifying a bean for autowiring.

@Service
public class OrderServiceImpl implements OrderService {
  private IStore store;
  @Inject
  @Named("retailStore")
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}

In the class qualified name is used for the dependency that should be injected using @Named annotation

That's all for the topic Spring Autowiring Using @Inject and @Named Annotations. 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