Setter Dependency Injection(SDI) vs Constructor Dependency Injection(CDI)

As we have already understood about SDI and CDI,

Now let’s see the differences between Setter Dependency Injection(SDI) and Constructor Dependency Injection(CDI)

1) The way we Inject
SDI usually happens after the object gets created by the container and by using the setter methods
Whereas CDI happens during the object creation by the container by passing dependencies as a parameter to the constructor.

2) Complete dependency injection vs Partial dependency injection

Since CDI happens during constructor execution, we need to pass all the necessary parameters to that constructor, so complete dependency injection is mandatory

Whereas SDI happens after constructor gets executed , so we can do either partial dependency injection or complete dependency injection based on the requirement.

Consider the class which has 3 attributes,

1
2
3
4
5
6
public class Person {
    private int id;
    private String name;
private String[] hobbies;
 
}
public class Person {
	private int id;
	private String name;
private String[] hobbies;

}

In CDI, we will have a constructor which takes 3 arguments like below

1
2
3
4
5
    public Person(int id, String name, String[] hobbies) {
        this.id = id;
        this.name = name;
        this.hobbies = hobbies;
    }
	public Person(int id, String name, String[] hobbies) {
		this.id = id;
		this.name = name;
		this.hobbies = hobbies;
	}

So if we want to create an object of it in spring, we need to supply all the dependencies for this constructor as below

1
2
3
4
5
6
7
8
9
10
11
<bean id="person" class="com.kb.di.Person">
    <constructor-arg value="1" type="int"/>
    <constructor-arg value="Raj"/> 
    <constructor-arg>
    <array>
    <value>Playing cricket</value>
    <value>Coding</value>
    <value>Reading books</value>
    </array>
</constructor-arg>
</bean>
<bean id="person" class="com.kb.di.Person">
	<constructor-arg value="1" type="int"/>
	<constructor-arg value="Raj"/> 
	<constructor-arg>
	<array>
	<value>Playing cricket</value>
	<value>Coding</value>
	<value>Reading books</value>
	</array>
</constructor-arg>
</bean>

If we try to inject the dependencies partially like below, we will get exception and we cannot create an object of it.

1
2
3
4
5
6
7
8
9
10
<bean id="person" class="com.kb.di.Person">
    <constructor-arg value="1" type="int"/>
    <constructor-arg>
    <array>
    <value>Playing cricket</value>
    <value>Coding</value>
    <value>Reading books</value>
    </array>
</constructor-arg>
</bean>
<bean id="person" class="com.kb.di.Person">
	<constructor-arg value="1" type="int"/>
	<constructor-arg>
	<array>
	<value>Playing cricket</value>
	<value>Coding</value>
	<value>Reading books</value>
	</array>
</constructor-arg>
</bean>

Since CDI matches with the parameters to the constructor, we will get below exception for the above partially injected case

Unsatisfied dependency expressed through constructor argument with index 1 of type [java.lang.String]: Could not convert constructor argument value of type [[Ljava.lang.Object;] to required type [java.lang.String]:

In SDI, we will have 3 setter methods one for each attribute like below

1
2
3
4
5
6
7
8
9
10
11
    public void setId(int id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }
	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setHobbies(String[] hobbies) {
		this.hobbies = hobbies;
	}

Since object gets created using default constructor here, so without any dependency injection, object gets created here.
So in SDI, we can do either partial injection as below

1
2
3
4
5
<bean id="person" class="com.kb.di.Person">
        <property name="id" value="1"></property>
        <property name="name" value="Raj"></property>
        
</bean>
<bean id="person" class="com.kb.di.Person">
		<property name="id" value="1"></property>
		<property name="name" value="Raj"></property>
		
</bean>

We have not injected the hobbies here,still we can create this bean

Or we can do full injection as below

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="person" class="com.kb.di.Person">
        <property name="id" value="1"></property>
        <property name="name" value="Raj"></property>
        <property name="hobbies">
            <array>
                <value>Playing cricket</value>
                <value>Coding</value>
                <value>Reading books</value>
            </array>
 
        </property>
</bean>
<bean id="person" class="com.kb.di.Person">
		<property name="id" value="1"></property>
		<property name="name" value="Raj"></property>
		<property name="hobbies">
			<array>
				<value>Playing cricket</value>
				<value>Coding</value>
				<value>Reading books</value>
			</array>

		</property>
</bean>

3) Readability

If we look at the readability of dependencies, we can prefer SDI, as in SDI we can see which property we are injecting based on the setter method and property tag.

1
<property name="id" value="1"></property>
<property name="id" value="1"></property>

which states clearly that value 1 is getting injected to the property called id.

Whereas in CDI, we cannot understand which value is injected into which attribute just by looking at the spring configuration file
For this we need to understand the order of the parameters defined in the constructor.

1
2
<constructor-arg value="1" type="int"/>
<constructor-arg value="Raj"/>
<constructor-arg value="1" type="int"/>
<constructor-arg value="Raj"/>

So in the above code, we cannot understand 1 is injected to which attribute and Raj is injected to which attribute, we need to look at the constrcutor in the corresponding class to understand it.

4) Overriding injected value

If we use both CDI and SDI , Spring container will override the CDI by SDI.

It means , if we define both SDI and CDI on the same attributes, then SDI value will be injected to that attribute by spring container.

Consider below example

1
2
3
4
5
6
<bean id="person" class="com.kb.di.Person">
     <constructor-arg value="1" type="int"/>
 
    <property name="id" value="11"></property>
        
    </bean>
<bean id="person" class="com.kb.di.Person">
	 <constructor-arg value="1" type="int"/>

	<property name="id" value="11"></property>
		
	</bean>

In the above code, we have injected value for id attribute using both CDI and SDI but the value which is injected by container will be the one injected by SDI.

So value 11 will be injected in the above case.

5) Flexibility

We can easily change the value by using SDI, it won’t create new instance unlike CDI.

Consider below example

1
2
3
4
<bean id="person" class="com.kb.di.Person">
    
    <property name="id" value="11"></property>
</bean>
<bean id="person" class="com.kb.di.Person">
	
	<property name="id" value="11"></property>
</bean>

Now access the bean and modify its value as below using setter method

1
2
3
4
5
6
7
8
9
10
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");  
        Person person = (Person)applicationContext.getBean("person");
 
        System.out.println(person.getId());
 
        person.setId(123);
 
        Person person1 = (Person)applicationContext.getBean("person");
 
        System.out.println(person1.getId());
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");  
        Person person = (Person)applicationContext.getBean("person");

        System.out.println(person.getId());

        person.setId(123);

        Person person1 = (Person)applicationContext.getBean("person");

        System.out.println(person1.getId());

In the above code, we are first loading the bean person which is loaded with the value 11 for id, after that we are changing it to 123 using setter method

So without creating new instance , we can change the bean value using setter method, so above code prints 11 and 123

Now assume , we are using CDI as below

1
2
3
4
<bean id="person" class="com.kb.di.Person">
    
    <constructor-arg value="1" type="int"/>
</bean>
<bean id="person" class="com.kb.di.Person">
	
	<constructor-arg value="1" type="int"/>
</bean>

If we don’t have setter method, and if we want to change its value using constructor then we need to create one more bean like below

1
2
3
4
<bean id="person1" class="com.kb.di.Person">
    
    <constructor-arg value="123" type="int"/>
</bean>
<bean id="person1" class="com.kb.di.Person">
	
	<constructor-arg value="123" type="int"/>
</bean>

So CDI makes the bean object immutable whereas SDI makes it mutable.

Which one to choose and when?

Based on all the above observation, One can think of using SDI most of the times over CDI however if we want to ensure complete dependency then it is preferable to use CDI.

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on