Tuesday, January 24, 2023

Spring Constructor Based Dependency Injection

In the post Dependency Injection in Spring I have already talked about dependency injection. In this post we'll explore one of the type of the dependency injection called Constructor based dependency injection in Spring.

Refer Setter-based dependency injection in Spring to know about another variant of dependency injection; Setter injection.

In Constructor based dependency injection, Spring IOC container invokes a class' constructor with arguments that represent the dependencies of that class.


Constructor Dependency Injection Example

Let's see an example of constructor dependency injection in Spring, there is one class called PayServiceImpl which is dependent on Payment class and also has a field amount of type int and those constructor arguments are injected through a constructor DI.

Payment Service class code

public class PayServiceImpl implements IPayService {
 private IPayment payment;
 private int amount;
 // Constructor
 PayServiceImpl(IPayment payment, int amount){
  this.payment = payment;
  this.amount = amount;
 }
 public void performPayment() {
  payment.executePayment(amount);
 }
}
public interface IPayService {
 void performPayment();
}

Code for the Payment class

public class CashPayment implements IPayment{
 public void executePayment(int amount) {
  System.out.println("Perform Cash Payment - " + amount);
 }
}
public interface IPayment {
 void executePayment(int amount);
}

Let's see the XML configuration file appcontext.xml, it has the declaration for the class beans and the constructor dependency injections-

<?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">
 
  <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />

  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="cashPaymentBean" />
      <constructor-arg value="20" />
  </bean>
</beans>

Here note that ref element is used while providing reference to another bean managed by the container. Since cashPaymentBean is declared in the configuration and will be managed by the container, it can be given as value of ref element.

For primitives and Strings value element is used.

If you want to see how it all works use this class-

public class App {
  public static void main( String[] args ){   
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
      ("appcontext.xml");
    IPayService bean = (IPayService) context.getBean("paymentBean");
    bean.performPayment();
    context.close();
  }
}

How Constructor arguments are resolved in Spring

When you use constructor dependency injection in Spring, constructor argument resolution matching occurs using the argument's type in Spring framework. The order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor and there they are matched using type.

Even if order is not same in bean definition and in the class' constructor arguments will be correctly matched. But arguments may not be resolved correctly if some ambiguity exists as example when the arguments are related by inheritance.

Let's say there are 2 classes CashPayment and CreditPayment and they are not related by inheritance.

public class CashPayment{
 public void executePayment() {
  System.out.println("Perform Cash Payment");
 }
}
public class CreditPayment{
 public void executePayment() {
  System.out.println("Performing credit payment");
 }
}

XML Config file

<?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">
 
  <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />
  <bean id="creditPaymentBean" class="org.netjs.prog.maven_spring.CreditPayment" />
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="creditPaymentBean" />
      <constructor-arg ref="cashPaymentBean" />    
  </bean>
</beans>

Here I have first provided reference for CreditPayment class and then for CashPayment class. Whereas in the PayServiceImpl class' constructor CashPayment is first and then CreditPayment. Still it will be resolved correctly.

 
public class PayServiceImpl implements IPayService {
 private CashPayment payment1;
 private CreditPayment payment2;
 private int amount;
 
 PayServiceImpl(CashPayment payment1, CreditPayment payment2){
  this.payment1 = payment1;
  this.payment2 = payment2;
 }
 public void performPayment() {
  payment1.executePayment();
  payment2.executePayment();
 }
}

Output

Perform Cash Payment
Performing credit payment

Ambiguity in case of constructor dependency injection

If we take the same classes as above and CashPayment and CreditPayment classes are related by inheritance then there will be ambiguity as they are of same type in that case.

public interface IPayment {
 void executePayment();
}
public class CreditPayment implements IPayment {
 public void executePayment() {
  System.out.println("Performing credit payment");
 }
}
public class CashPayment implements IPayment{
 public void executePayment() {
  System.out.println("Perform Cash Payment"); 
 }
}
public class PayServiceImpl implements IPayService {
 private IPayment payment1;
 private IPayment payment2;

 PayServiceImpl(IPayment payment1, IPayment payment2){
  this.payment1 = payment1;
  this.payment2 = payment2;
 }
 public void performPayment() {
  payment1.executePayment();
  payment2.executePayment();
 }
}
<?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">
 
  <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />
  <bean id="creditPaymentBean" class="org.netjs.prog.maven_spring.CreditPayment" />
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="creditPaymentBean" />
      <constructor-arg ref="cashPaymentBean" />    
  </bean>
</beans>
public class App {
  public static void main( String[] args ){   
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
      ("appcontext.xml");
    IPayService bean = (IPayService) context.getBean("paymentBean");
    bean.performPayment();
    context.close();
  }
}

Output

Performing credit payment
Perform Cash Payment

Here it can be seen that in class PayServiceImpl both CashPayment and CreditPayment have reference through IPayment, so type is same thus whichever class comes first in the bean definition will be configured first.
You can change the reference in the config file and then CashPayment will be called first.

Injecting primitive values using constructor dependency injection

When a primitive type or String is injected using constructor dependency injection in Spring, we need to explicitly specify the type of the constructor arg using the type attribute.

Let's first see an example where type attribute is not used.

public class AmountBean {
 private int amount;
 private String year;
 public AmountBean(int amount, String year) {
  this.amount = amount;
  this.year = year;
 }
 
 public void displayValue(){
  System.out.println("amount - " + amount);
  System.out.println("year - " + year);
 }
}

In the XML config file you can add the bean declaration for the AmountBean class.

<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg value="2015" />    
   <constructor-arg value="200" />
</bean>

In the configuration I have provided value for the string argument year first and then for integer argument amount.

 
public class App {
  public static void main( String[] args ){   
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext
      ("appcontext.xml");
    AmountBean bean = (AmountBean)context.getBean("amountBean");
    bean.displayValue();
    context.close();
  }
}

Output

amount - 2015
year - 200

It can be seen that without specifying the type attribute, container can't resolve it correctly. 2015 is assigned to integer arg and 200 is assigned to String arg.

Using type attribute in Constructor Dependency injection

type matching can be provided by explicitly specifying the type of the constructor argument using the type attribute.

 
<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg type="java.lang.String" value="2015" />    
   <constructor-arg type="int" value="200" />
</bean>

Using index attribute in Constructor Dependency injection

Another option with constructor dependency injection is using the index attribute to explicitly specify the index of constructor arguments. Note that index is 0 based.

<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg index="1" value="2015" />    
   <constructor-arg index="0" value="200" />
</bean>

Here I am explicitly saying that first value should be assigned to the second constructor argument and the second value to the first constructor argument.

That's all for this topic Spring Constructor Based Dependency Injection. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring XML Configuration Example
  2. Spring Java Configuration Example Using @Configuration
  3. Spring Component Scan Example
  4. Dependency Injection Using factory-method in Spring
  5. Wiring Collections in Spring

You may also like-

  1. Difference Between component-scan And annotation-config in Spring
  2. Bean Scopes in Spring With Examples
  3. Spring Transaction Management Example - @Transactional Annotation and JDBC
  4. Creating a Maven project in Eclipse
  5. Method Reference in Java
  6. Synchronization in Java - Synchronized Method And Block
  7. Lock Striping in Java Concurrency
  8. Format Date in Java Using SimpleDateFormat