Dependency Injection and IOC

Dependency Injection and Inversion of Control

An application consists of multiple objects. Multiple objects collaborate and together perform business operations in the application. Thus one object may depend on other objects to perform a business operation that is called dependency.

Here is example where three objects Order, Payment and Inventory collaborate and perform a business operation “Books a Ticket”. Order object books the order and make payment with help of Payment object. After deducting payment order updates inventory with help of Inventory object. In order to book a ticket Order requires Payment and Inventory objects thus order is depend on Payment and Inventory objects.

Figure: Object Dependency

Non-IOC Dependency

Regular (Non-IOC) or conventional way to resolve the dependency is to create the dependent objects and call their methods. Order has to create instances of payment and inventory classes and use them.

Figure: Non-IOC Dependency

Here is method placeOrder() of Order class that creates dependent objects and use them.

public class Payment {
   public double makePayment(double amt) {
     balance = balance - amt;
     return balance;
   }
}
public class Inventory {
   public int sold(int qty) {
    stock -= qty;
    return stock;
  }
}
public class Order {
  public void placeOrder(int qty, double price) {
     Payment p = new Payment(); //create payment object
     Inventory i = new Inventory(); //create inventory object
     amount = qty * price;
     p.makePayment(amount);
     i.sold(qty);
     System.out.println("Balance: " + p.getBalance());
     System.out.println("Stock : " + i.getStock());
  }
}

IOC Dependency Injection

Inversion of Control (IOC) says, now object does not need to create its dependencies, dependencies will be injected by spring IOC container using constructors or setter methods. Object defines its dependency using XML or annotations. IOC helps a developer to decouple object creation logic from business logic.

Figure: IOC Dependency Injection

Here Order defines setter injection methods. XML configuration injects Payment and Inventory objects into Order.

public class Order{
 
  private Payment p= null;
  private Inventory i= null;
 
  public void setPayment(Payment p) {
     this.p= p;
  }
   public void setInventory(Inventory i) {
     this.i= i;
   }
  public void placeOrder(int qty, double price) {
     amount = qty * price;
     p.makePayment(amount);
     i.sold(qty);
     System.out.println("Balance: " + p.getBalance());
     System.out.println("Stock : " + i.getStock());
  }
}

Here is metadata in applicationContext.xml file to inject dependencies in Order class.

<bean name="payment" scope="prototype" class="Payment" />
<bean name="inventory" scope="prototype" class="Inventory" />
<bean name="order" scope="prototype" class="Order">
   <property name="payment" ref="payment" />
   <property name="inventory" ref="inventory" />
</bean>

Now get order object from spring IOC container:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Order o = (Order) context.getBean("order");
o.placeOrder(2,500);

Types of Dependency Injection

There are two types of DI.

1. Constructor-based dependency injection

Dependencies are injected with the help of bean constructors.Constructors are defined in the bean to accept dependent objects. For example Customer bean is dependent on Address bean. In order to inject address objects one parameterized constructor is defined in Customer class. Constructor accepts shipping and billing objects of Address class. XML configuration uses <constructor-arg> tag to inject dependencies using constructor.

public class Address {
..
}
public class Customer {
    private Address billingAddress = null;
    private Address shippingAddress = null;
    public Customer(Address billingAddress, Address shippingAddress) {
        this.billingAddress = billingAddress;
        this.shippingAddress = shippingAddress;
    }
    ..
}

Configuration

<bean id="address" scope="prototype" class="com.sunilos.book.spring.bean.Address"/>
<bean id="customer" scope="prototype" class="com.sunilos.book.spring.bean.Customer">
    <constructor-arg>
        <ref bean="address" />
    </constructor-arg>
    <constructor-arg>
        <ref bean="address" />
    </constructor-arg>
</bean>

2. Setter-based dependency injection

Dependencies can be injected with the help of setter methods. Bean has to define setter methods for dependent objects.

For example order bean is depend on inventory and payment beans. Order bean defines two setter methods, one for payment bean and second for inventory bean to inject their objects. XML configuration uses <property> tags to inject inventory and payment beans into order bean.

public class Inventory {
  ..
}
public class Payment {
  ..
}
public class Order {
  private Payment payment = null;
  private Inventory inventory = null;
  public void setPayment(Payment payment) {
     this.payment = payment;
  }
  public void setInventory(Inventory inventory) {
     this.inventory = inventory;
  }
  ..
}

<bean name="payment" scope="prototype" class="com.sunilos.spring.bean.Payment" />
<bean name="inventory" scope="prototype" class="com.sunilos.spring.bean.Inventory" />
<!-- Inject dependent bean references using setter injection -->
<bean name="order" scope="prototype" class="com.sunilos.spring.bean.Order">
   <property name="payment" ref="payment" />
   <property name="inventory" ref="inventory" />
</bean>


FAQ

Q: What is DI?

Q: How many types of DI are there?

Q: What are the differences between by-name and by-type dependency injection

Q: What are the differences between explicit and implicit auto-wiring ?

A: Explicit auto-wiring is done by XML tags and implicit auto-wiring is done by @Autowired annotation. If both are done then explicit auto-wiring is considered by container.