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-contextdependency inpom.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="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:p="https://www.springframework.org/schema/p"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://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.
