In this blog post we’ll learn about Dependency Injection (DI) and how to use it. We can do this using constructor and setter injection. Also, I recommend checking out this YouTube video by Ryan Schachte if it’s still available.
DI demos:
In my first Spring basics blog post we explored how to create Spring beans with XML configuration.
<bean id="blogPostService" class="com.maxdemaio.service.BlogPostService" />
Using Spring with this config would be similar as the below Java code. An instance is created and initialized with the default values using the default constructor. But, if we use Spring with the above config, our code will be loosely coupled!
BlogPostService blogPostService = new BlogPostService();
We can also initialize beans with specific values in Spring with DI. There are actually two ways we can do this. We can use constructor injection and setter injection.
Constructor Injection
Primitive Values
Let’s consider this BlogPostService class to understand constructor injection:
package com.maxdemaio.service;
public class BlogPostService {
private int wordCount;
public BlogPostService(int wordCount) {
this.wordCount = wordCount;
}
// ...
}
Now, to define a bean in our XML configuration to initialize values we can write the following:
<bean id="blogPostService" class="com.maxdemaio.service.BlogPostService" >
<constructor-arg value="8"/>
</bean>
We’ll need a parameterized constructor and a constructor-arg
in the bean definition. Also, if we have more than one parameter in our constructor, we can pass constructor-arg
tags in the same order.
We can also use a name
attribute in our constructor-arg
tag to specify constructor parameters. This avoids having to pass arguments in order. We can also specify the type
attribute to avoid order to an extent as well. Spring will convert the String value to the appropriate type if compatible. If it cannot convert the value we’ll get an exception.
Non-Primitive Values
So far we’ve seen how we can do constructor injection with primitive data types. Now we’ll take a look at how to do so with non-primitive data types. For example, let’s take our BlogPostService
class. We’ll make it dependent upon a PostGenerator
object type to create a “cool” or “witty” blog post.
package com.maxdemaio.demo;
public interface PostGenerator {
public String generatePost(int wordCount);
}
package com.maxdemaio.demo;
public class CoolPostGenerator implements PostGenerator {
@Override
public String generatePost(int wordCount) {
return "Generated cool post with " + wordCount + " words";
}
}
package com.maxdemaio.demo;
public class WittyPostGenerator implements PostGenerator {
@Override
public String generatePost(int wordCount) {
return "Generated witty post with " + wordCount + " words";
}
}
package com.maxdemaio.service;
public class BlogPostService {
private PostGenerator gen;
private int wordCount;
public BlogPostService(PostGenerator gen, int wordCount) {
System.out.println("Parameterized Constructor");
this.gen = gen;
this.wordCount = wordCount;
}
public void generatePost() {
System.out.println(gen.generatePost(wordCount));
}
}
We can see that the PostGenerator
property of the BlogPostService
class has not been initialized with any values. We can let Spring take care of the configuration for us.
How in the world would we configure this to generate us a “cool” or “witty” blog post? Let’s look at an example where we generate a “cool” blog post using the ref
attribute in our constructor-arg
tag.
<?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.xsd>">
<bean id="blogPostService" class="com.maxdemaio.demo.BlogPostService">
<constructor-arg name="gen" ref="coolPostGenerator"/>
<constructor-arg name="wordCount" value="150"/>
</bean>
<bean id="coolPostGenerator" class="com.maxdemaio.demo.CoolPostGenerator"/>
<bean id="wittyPostGenerator" class="com.maxdemaio.demo.WittyPostGenerator"/>
</beans>
Setter Injection
For Setter Injection, Spring invokes the setter methods of a class to initialize the properties after invoking its default constructor.
To start, let’s look at how we can inject primitive values into a class’s properties with setter injection. We’ll use the property
tag within the XML configuration.
package com.maxdemaio.demo;
public class BlogPostService {
private PostGenerator gen;
private int wordCount;
public BlogPostService() {}
public int getwordCount() {
return wordCount;
}
public void setwordCount(int wordCount) {
this.wordCount = wordCount;
}
public PostGenerator getgen() {
return gen;
}
public void setgen(PostGenerator gen) {
this.gen = gen;
}
public void generatePost() {
System.out.println(gen.generatePost(wordCount));
}
}
<?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.xsd>">
<bean id="blogPostService" class="com.maxdemaio.demo.BlogPostService">
<property name="gen" ref="wittyPostGenerator"/>
<property name="wordCount" value="500"/>
</bean>
<bean id="coolPostGenerator" class="com.maxdemaio.demo.CoolPostGenerator"/>
<bean id="wittyPostGenerator" class="com.maxdemaio.demo.WittyPostGenerator"/>
</beans>
For setter injection, we need a default constructor and setter methods of dependent properties. Spring will use the default constructor to create a bean. Then, Spring invokes the setter method of the respective property. This is based on the name
attribute to initialize the values. Also, property
tags are mandatory in the bean definition.
Collection Values
We’ve learning how to inject primitive and non-primitive values with the value and ref attributes. To inject collections, Spring supports Java Collection types such as List, Set, Map, and Properties.
package com.maxdemaio.demo;
import java.util.List;
public class BlogPostService {
private List<PostGenerator> gens;
public void setGens(List<PostGenerator> gens) {
this.gens = gens;
}
public List<PostGenerator> getGens() {
return gens;
}
}
<?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.xsd>">
<bean id="blogPostService" class="com.maxdemaio.demo.BlogPostService">
<property name="gens">
<list>
<ref bean="coolPostGenerator" />
<ref bean="wittyPostGenerator" />
</list>
</property>
</bean>
<bean id="coolPostGenerator" class="com.maxdemaio.demo.CoolPostGenerator" />
<bean id="wittyPostGenerator" class="com.maxdemaio.demo.WittyPostGenerator" />
</beans>
package com.maxdemaio.demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
BlogPostService srv = (BlogPostService) context.getBean("blogPostService");
System.out.println(srv.getGens());
}
}
Conclusion
We’ve learned how to use constructor and setter injection in Spring. We can do so with primitive, non-primitive, or collection values. To conclude, we’ll take a look at the difference between the two and when you’d want to use one over the other:
Constructor Injection | Setter Injection |
---|---|
Dependency injection via parameterized constructor | Dependency Injection via setter methods after invoking the default constructor |
Need parameterized constructor in the POJO class | Need default constructor and setter methods in the POJO class |
Good for mandatory dependencies and immutable dependencies. Concise (pass several parameters once). | Optional/changeable dependencies. Avoids circular dependencies and cycles. |