June 29, 2022

Spring Dependency Injection With Examples

This post shows what is dependency injection in Spring framework along with examples of injecting dependencies using XML configuration as well as using annotations.

What is dependency injection

If you create even a simple application that will have few objects interacting with each other to implement the required functionality. Generally in a Java application you use new operator to create objects that are required in a class. For example in Class A if object of class B is required then you may create it as-

class A{
  Private B obj;
  public A(){
    obj = new B();
  }
  ...
  ...
}

By providing dependency this way your classes are-

  1. Tightly coupled making it difficult to replace objects
  2. Not easy to test- Explicit dependency makes it difficult to mock objects thus making unit testing difficult.

Ideally you would like classes to be decoupled from each other making it easy to reuse them and also making it easy to mock the dependencies for unit testing.

That’s what dependency injection does, rather than class itself creating or finding the other objects (dependencies) those object dependencies are injected into the class. This process is fundamentally the inverse of the bean itself controlling the instantiation or location of its dependencies on its own therefore Dependency injection is also considered as one of the subtype of Inversion of Control (IoC).

Spring dependency injection

Dependency injection is one of the core concept of Spring framework and it makes DI easy by providing a standard way to provide configuration metadata which is then used by Spring container to instantiate objects and wire the dependencies.

Dependency injection in Spring exists in two major variants-

For configuring dependencies you can either use XML configuration or annotation based configuration, we’ll see examples of both of these ways.

Spring dependency injection example with XML configuration

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 dependency for store has to be injected.

Spring framework recommends that dependencies are on interfaces or abstract base classes, so that stub or mock implementations can easily be used in unit tests, so we’ll code to interfaces.

public interface IStore {
  public void doPurchase();
}
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing online purchase of Items");
  }
}
public class RetailStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing purchase of Items from a brick and mortar store");
  }
}
public class Order {
  private IStore store;
  // Constructor dependency
  Order(IStore store){
    this.store = store;
  }
  public void buyItems() {
    store.doPurchase();
  }
}
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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
  <!-- Store bean --> 
  <bean id="store" class="com.knpcode.SpringProject.OnlineStore" />           
  <!-- Order bean with dependencies -->
  <bean id="orderBean" class="com.knpcode.SpringProject.Order">
    <constructor-arg ref="store" />
  </bean>
</beans>

In the XML configuration there is bean definition for OnlineStore and Order classes. In orderBean, reference of store is passed as a constructor argument.

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

import org.springframework.context.support.ClassPathXmlApplicationContext;

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

If you want to use setter dependency injection then Order class should have a setter method for the property.

public class Order {
  private IStore store;

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

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

Change in XML configuration is also needed for setter dependency. Now property element is used to define dependency.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-4.0.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" />
  </bean>
</beans>

Spring dependency injection example with annotations

XML configuration is not the only way to configure dependencies, you can do it using annotations like @Componenent, @Service to annotate dependencies and @Autowired to denote that dependency has to be wired.

To indicate that auto discovery of the bean has to be done in the XML you just need to define context:component-scan with base-package. In base-package you can pass the package where your classes resides so that these classes are automatically scanned. You can completely ditch the XML and use Java configuration instead.

import org.springframework.stereotype.Service;

@Service
public class OnlineStore implements IStore {
  public void doPurchase() {
    System.out.println("Doing online purchase of Items");
  }
}
import org.springframework.stereotype.Service;

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

@Service
public class Order {
  private IStore store;
  @Autowired
  @Qualifier("retailStore")
  public void setStore(IStore store) {
    this.store = store;
  }

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

Now the classes are annotated with @Service annotation that denotes that these are Spring managed component and will be automatically discovered when component scanning is done.

@Autowired annotation on the setter method denotes that the dependency has to be wired automatically. 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"

XML configuration won’t have any dependencies now only <context:component-scan> tag to indicate that bean have to automatically discovered using component scanning.

<?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>

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

import org.springframework.context.support.ClassPathXmlApplicationContext;

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

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