January 12, 2024

Setter Dependency Injection in Spring

In the post dependency injection in Spring we have already gone through the concept of dependency injection, in this post we’ll see in details one of the type of dependency injection- Setter dependency injection in Spring.

For another type of dependency injection, Constructor dependency injection check this post- Constructor Dependency Injection in Spring

Spring Setter dependency injection

In setter based DI Spring container calls setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.

For configuring setter based dependencies you can use XML configuration as well as annotations. We’ll see examples of doing it using both of these ways.

Spring Setter dependency injection Example

In the example there is a class to place orders called Order and purchase can be done from an online store or a retail store. In Order class dependencies for the properties are injected using setter dependency injection.

public interface IStore {
  public void doPurchase(int items);
}
public class OnlineStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing online purchase of " + items + " Items");
  }
}
public class RetailStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing purchase of " + items + " Items from a brick and mortar store");
  }
}
public class Order {
  private IStore store;
  private int items;

  public void setStore(IStore store) {
    this.store = store;
  }
  public void setItems(int items) {
    this.items = items;
  }

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

In the Order class these are two properties one reference to Istore type and another an int. There are setter methods for those properties which will be called by the Spring container to set the configured values.

If you are using XML configuration then beans are defined as given in the following XML-

<?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">
  <!-- Store bean --> 
  <bean id="store" class="com.knpcode.SpringProject.RetailStore" />           
  <!-- Order bean with dependencies -->
  <bean id="orderBean" class="com.knpcode.SpringProject.Order">
    <property name="store" ref="store" />
    <property name="items" value="20" />
  </bean>
</beans>

For providing setter dependencies <property> tag is used.

  • When dependency is for another bean “ref” attribute is used.
  • For any primitive type or String “value” attribute is used.

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");
    Order order = (Order) context.getBean("orderBean");
    order.buyItems();
    // close the context
    context.close();
  }
}

Spring Setter dependency injection using annotations

If you want to configure setter dependencies in Spring using annotations then you will have to use @Service or @Component annotation with the classes to signify that these are Spring managed component and will be automatically discovered when component scanning is done.

Annotate setter methods with @autowired to inject the setter dependencies automatically.

@Service
public class OnlineStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing online purchase of " + items + " Items");
  }
}
@Service
public class RetailStore implements IStore {
  public void doPurchase(int items) {
    System.out.println("Doing purchase of " + items + " Items from a brick and mortar store");
  }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class Order {
  private IStore store;
  @Value("20")
  private int items;
  @Autowired
  @Qualifier("onlineStore")
  public void setStore(IStore store) {
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase(items);
  }
}

Since there are two objects of type store so @Qualifier annotation has been used to tell which bean has to be wired otherwise you will get an error "No qualifying bean of type 'com.knpcode.SpringProject.IStore' available"

Autowiring works with references only so primitive value is provided using @Value annotation.

If you want to use XML to set up component scanning for automatically discovering beans then it can be done using following XML.

<?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" />
</beans>

XML configuration won’t have any dependencies now only <context:component-scan> tag.

Constructor-based or setter-based Dependency injection

As per Spring documentation constructor based DI is preferred over setter-based DI.

"Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency."

That's all for the topic Setter Dependency Injection in Spring. 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