Learnitweb

Setter Based Dependency Injection in Spring

In this article, we’ll discuss Setter-Based Dependency Injection within Spring.

Setter-based dependency is accomplished by the container calling setter methods on beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.

Let us now understand setter-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 when setter-based dependency in used:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Component
public class Car {
//car has dependency on Engine
Engine engine;
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
public Engine getEngine() {
return engine;
}
}
@Component public class Car { //car has dependency on Engine Engine engine; @Autowired public void setEngine(Engine engine) { this.engine = engine; } public Engine getEngine() { return engine; } }
@Component
public class Car {
	//car has dependency on Engine
	Engine engine; 
	
	@Autowired
	public void setEngine(Engine engine) {
		this.engine = engine;
	}
	
	public Engine getEngine() {
		return engine;
	}
}

This is annotation based configuration.

We can configure the same in XML using ref.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<bean id="audi" class="com.learnitweb.components.Car">
<property name="engine">
<ref bean="engine" />
</property>
</bean>
<bean id="audi" class="com.learnitweb.components.Car"> <property name="engine"> <ref bean="engine" /> </property> </bean>
<bean id="audi" class="com.learnitweb.components.Car">
	<property name="engine">
		<ref bean="engine" />
	</property>
</bean>

There is more shorter or cleaner way of writing the above configuration:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<bean id="audi" class="com.learnitweb.components.Car">
<property name="engine" ref="engine"></property>
</bean>
<bean id="audi" class="com.learnitweb.components.Car"> <property name="engine" ref="engine"></property> </bean>
<bean id="audi" class="com.learnitweb.components.Car">
	<property name="engine" ref="engine"></property>
</bean>

When the engine is passed into the car by calling the setter method, it is called as setter-based dependency injection.

Let us now write the complete program.

Before we demonstrate this, we’ll do the following:

  • Specify spring-context dependency in pom.xml.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.8</version> </dependency>
<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.

Setter based dependency injection – Annotation based configuration

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

Engine.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
//getter, setter and toString() methods omitted for brevity
}
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; } //getter, setter and toString() methods omitted for brevity }
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;
	}
	
	//getter, setter and toString() methods omitted for brevity
}

We’ll now create Car class. @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 setter method to autowire when used as a Spring bean.

Car.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 void setEngine(Engine engine) {
this.engine = engine;
}
public Engine getEngine() {
return engine;
}
}
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 void setEngine(Engine engine) { this.engine = engine; } public Engine getEngine() { return engine; } }
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 void setEngine(Engine engine) {
		this.engine = engine;
	}
	
	public Engine getEngine() {
		return engine;
	}
}

Let us do the configuration now. @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.

AppConfig.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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);
}
}
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); } }
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);
    }
}

Let’s now test our constructor based dependency injection.

App.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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);
}
}
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); } }
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);
    }
}

Setter based dependency injection – XML based configuration

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

Car.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.components;
import com.learnitweb.dependency.Engine;
public class Car {
Engine engine;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public String toString() {
return "Car [engine=" + engine + "]";
}
}
package com.learnitweb.components; import com.learnitweb.dependency.Engine; public class Car { Engine engine; public Engine getEngine() { return engine; } public void setEngine(Engine engine) { this.engine = engine; } @Override public String toString() { return "Car [engine=" + engine + "]"; } }
package com.learnitweb.components;

import com.learnitweb.dependency.Engine;

public class Car {
	Engine engine; 
	
	public Engine getEngine() {
		return engine;
	}
	
	public void setEngine(Engine engine) {
		this.engine = engine;
	}

	@Override
	public String toString() {
		return "Car [engine=" + engine + "]";
	}
}
App.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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);
}
}
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); } }
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);
    }
}
Engine.java
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.dependency;
public class Engine {
String manufacturer;
String type;
int cylinders;
public Engine() {
super();
}
//getter setters and toString() methods removed for brevity
}
package com.learnitweb.dependency; public class Engine { String manufacturer; String type; int cylinders; public Engine() { super(); } //getter setters and toString() methods removed for brevity }
package com.learnitweb.dependency;

public class Engine {
	String manufacturer;
	String type;
	int cylinders;
	
	public Engine() {
		super();
	}
	
	//getter setters and toString() methods removed for brevity
}
app-context.xml
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?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">
<property name="engine">
<ref bean="engine"/>
</property>
</bean>
<bean id="engine" class="com.learnitweb.dependency.Engine">
<property name="manufacturer" value="audi" />
<property name="type" value="automatic" />
<property name="cylinders" value="4" />
</bean>
</beans>
<?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"> <property name="engine"> <ref bean="engine"/> </property> </bean> <bean id="engine" class="com.learnitweb.dependency.Engine"> <property name="manufacturer" value="audi" /> <property name="type" value="automatic" /> <property name="cylinders" value="4" /> </bean> </beans>
<?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">
		<property name="engine">
        	<ref bean="engine"/>
    	</property>
	</bean>

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

When to use setter based injection?

Setter based dependency injection should be used for optional dependencies. @Required annotation on a setter method can be used to make the property be a required dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.

Setter-based dependency injection also helps in resolving the circular dependency problem while using constructor based dependency injection.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If we configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException. In such situation, setters can be used rather than constructors.