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:

@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.

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

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

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.

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.

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.

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.

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.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.dependency;

public class Engine {
	String manufacturer;
	String type;
	int cylinders;
	
	public Engine() {
		super();
	}
	
	//getter setters and toString() methods removed for brevity
}
<?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.