Learnitweb

Constructor Based Dependency Injection in Spring

1. Introduction to constructor-based dependency injection

In this article, we’ll discuss Constructor-Based Dependency Injection within Spring. This type of dependency injection means – the required dependencies are passed into a class at the time of creation of object (or instantiation).

The fields or member variables in a class are the dependencies which a class needs. In constructor based dependency injection, these dependencies are passed into the class at the time of instantiation. Since we know that the constructor is used for instantiation, these dependencies are passed into the class when it’s constructor is executed.

When we use Spring, the injection of dependencies is taken care by the Spring.

Let us now understand constructor-based dependency injection with the help of an example.

Suppose we have a car. Every car needs an engine. We can say that car has a dependency on engine. When this relationship is represented with the help of classes will look something like the following:

public class Car {
	Engine engine; 
	
    public Car(Engine engine) {
        this.engine = engine;
    }
}

When the engine is passed into the car at the time of instantiation with the help of constructor, it is called as constructor-based dependency injection.

Before we demonstrate this, we have to do the following:

  • Specify spring-context dependency in pom.xml.
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.3.8</version>
</dependency>
  • Configuration of our application either by Configuration file or XML file.

2. Annotation based configuration

We’ll first create the Engine class. This class has three fields, manufacturer, type and cylinders.

package com.learnitweb.dependency;

public class Engine {
	String manufacturer;
	String type;
	int cylinders;
	
	public Engine(String manufacturer, String type, int cylinders) {
		super();
		this.manufacturer = manufacturer;
		this.type = type;
		this.cylinders = cylinders;
	}
	
	public String getManufacturer() {
		return manufacturer;
	}
	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public int getCylinders() {
		return cylinders;
	}
	public void setCylinders(int cylinders) {
		this.cylinders = cylinders;
	}

	@Override
	public String toString() {
		return "Engine [manufacturer=" + manufacturer + ", type=" + type + ", cylinders=" + cylinders + "]";
	}
}

We’ll now create Car class.

package com.learnitweb.components;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.learnitweb.dependency.Engine;

@Component
public class Car {
	Engine engine; 
	
	@Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }

	@Override
	public String toString() {
		return "Car [engine=" + engine + "]";
	}
}

@Component indicates that an annotated class is a “component”. Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
@Autowired indicates the constructor to autowire when used as a Spring bean.

Let’s do the configuration now.

package com.learnitweb.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.learnitweb.dependency.Engine;

@Configuration
@ComponentScan("com.learnitweb.components")
public class AppConfig {
	
	@Bean
    public Engine engine() {
        return new Engine("Audi", "automatic", 4);
    }
}

@Configuration indicates that this is the configuration class.

@ComponentScan is used to specify the packages to scan.

@Bean is used to declare a bean. Simply annotate a method with the @Bean annotation. When JavaConfig encounters such a method, it will execute that method and register the return value as a bean within a BeanFactory.

Let’s now test our constructor based dependency injection.

package com.learnitweb.ConstructorInjectionExample;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.learnitweb.components.Car;
import com.learnitweb.configuration.AppConfig;

public class App {
    public static void main( String[] args ) {
    	ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    	Car car = context.getBean(Car.class);
    	System.out.println(car);
    }
}

Output

Car [engine=Engine [manufacturer=Audi, type=automatic, cycliners=4]]

3. XML based configuration

In XML based configuration, we’ll use XML to configure. We can remove all annotations used in previous example and Configuration class.

package com.learnitweb.components;

import com.learnitweb.dependency.Engine;

public class Car {
	Engine engine; 
	
    public Car(Engine engine) {
        this.engine = engine;
    }

	@Override
	public String toString() {
		return "Car [engine=" + engine + "]";
	}
}
package com.learnitweb.dependency;

public class Engine {
	String manufacturer;
	String type;
	int cylinders;
	
	public Engine(String manufacturer, String type, int cylinders) {
		super();
		this.manufacturer = manufacturer;
		this.type = type;
		this.cylinders = cylinders;
	}
	
	public String getManufacturer() {
		return manufacturer;
	}
	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public int getCylinders() {
		return cylinders;
	}
	public void setCylinders(int cylinders) {
		this.cylinders = cylinders;
	}

	@Override
	public String toString() {
		return "Engine [manufacturer=" + manufacturer + ", type=" + type + ", cylinders=" + cylinders + "]";
	}
}
package com.learnitweb.constructorInjectionXml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.learnitweb.components.Car;

public class App {
    public static void main( String[] args ){
    	ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
		Car car = ctx.getBean("audi", Car.class);
		System.out.println(car);
    }
}
<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="audi" class="com.learnitweb.components.Car">
		<constructor-arg index="0" ref="engine" />
	</bean>

	<bean id="engine" class="com.learnitweb.dependency.Engine">
		<constructor-arg index="0" value="audi" />
		<constructor-arg index="1" value="automatic" />
		<constructor-arg index="2" value="4" />
	</bean>
</beans>

constructor-arg is used to provide the constructor based dependencies. constructor-arg’s index attribute accepts a literal value and indicates the index position of argument in constructor. ref attribute is used to refer to another bean. index and type are optional attributes which help in resolving ambiguity when constructor takes multiple arguments of the same type.

Note

As of Spring 4.3, classes with a single constructor can omit the @Autowired annotation.

4. Constructor Argument Resolution

Constructor argument resolution matching occurs by using the argument’s type. The order in which the arguments are supplied to the constructor of the bean to be instantiated is same as the order in which the constructor arguments are defined in a bean definition.

Both the following are valid:

public Engine(String manufacturer, String type, int cycliners) {
	super();
	this.manufacturer = manufacturer;
	this.type = type;
	this.cycliners = cycliners;
}
<bean id="engine" class="com.learnitweb.dependency.Engine">
	<constructor-arg value="audi" />
	<constructor-arg value="automatic" />
	<constructor-arg value="4" />
</bean>

4.1 Constructor argument type matching

We can explicitly specify the type of the constructor argument by using the type attribute, to remove any ambiguity. For example:

<bean id="engine" class="com.learnitweb.dependency.Engine">
	<constructor-arg type="java.lang.String" value="audi" />
	<constructor-arg type="java.lang.String" value="automatic" />
	<constructor-arg type="int" value="4" />
</bean>

4.2 Constructor argument index

You can use the index attribute to specify explicitly the index of constructor arguments. For example:

<bean id="engine" class="com.learnitweb.dependency.Engine">
	<constructor-arg index="0" value="audi" />
	<constructor-arg index="1" value="automatic" />
	<constructor-arg index="2" value="4" />
</bean>

The index is 0-based.

4.3 Constructor argument name

There is one more attribute name, which is the constructor parameter name and can be used to remove ambiguity. For example:

<bean id="engine" class="com.learnitweb.dependency.Engine">
	<constructor-arg name="manufacturer" value="audi" />
	<constructor-arg name="type" value="automatic" />
	<constructor-arg name="cylinders" value="4" />
</bean>

5. When to use constructor injection?

It is advised to use constructor based dependency injection for all mandatory dependencies. Constructor injection ensures all mandatory dependencies have been satisfied and the object is never instantiated in an invalid state.