We have already discussed dependency injection in the previous article. Let’s now discuss the dependency injection types supported by Spring.
Spring supports three types of dependency injection –
- Field Injection
- Setter Injection
- Constructor Injection
Field Injection
As the name indicates, in this case we declare the @Autowired
annotation at field and Spring directly injects the dependency on field without using constructor or setter method. Internally Spring uses reflection to set the field values.
Suppose we have a Vehicle
class that depends on the Engine
class to start. Let’s first create an Engine interface and an implementation of that interface.
public interface Engine {
void start();
}
@Component
public class EngineImpl implements Engine {
public void start() {
System.out.println("Starting engine...");
}
}
Now, if Vehicle class depends on Engine to start, probably we will have a field declared in Vehicle class of type Engine. As we said, in case of field injection, we declare the @Autowired
annotation directly at the field. So, let’s create a Vehicle
class with an engine field annotated with @Autowired annotation.
@Service
public class Vehicle {
@Autowired
private Engine engine;
public void start() {
engine.start();
}
}
Spring doesn’t care about field visibility as reflection is used to set the field value. Spring will find an implementation of the Engine interface (in our case EngineImpl) and assign an instance of that class to the engine field.
Setter Injection
In case of setter injection, we have to add the @Autowired
annotation to the setter method.
@Service
public class Vehicle {
private Engine engine;
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
In this case also, Spring doesn’t care about setter method visibility as reflection is used to call the setter method. But almost all the time we make the setter methods public.
Constructor Injection
In case of constructor injection, we have to add the @Autowired
annotation to the constructor.
@Service
public class Vehicle {
private Engine engine;
@Autowired
public Vehicle(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
In case you have a single constructor and that constructor is not annotated with @Autowired
annotation, Spring will use that constructor and inject the dependency as there is no other way to construct an object. So, the following code will also behave like the previous code.
@Service
public class Vehicle {
private Engine engine;
public Vehicle(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
But if you have multiple constructor without @Autowired
annotation and among them one is no argument constructor, spring will always prefer the no argument constructor.
@Service
public class Vehicle {
private Engine engine;
public Vehicle() {}
public Vehicle(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
Of course this will throw NullPointerException
at the highlighted line because the default constructor
is used to create a Vehicle class instance which doesn’t set the engine
field.
To resolve this we have to use @Autowired
annotation to the other constructor. Because when Spring identifies multiple constructor, but one of them is annotated with @Autowired
annotation, Spring will use that. Only if none of them are annotated with the @Autowired
annotation, Spring will use the no argument constructor. So the following code will work just fine.
@Service
public class Vehicle {
private Engine engine;
public Vehicle() {}
@Autowired
public Vehicle(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}
Now let’s assume we have multiple constructor without no argument constructor and none of them are annotated with @Autowired
annotation. Spring won’t be able to decide which constructor to use as there is no no-arg
constructor. So, Spring will say sorry with following error message –
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.kcs.service.Vehicle]: No default constructor found; |
Similar confusion will happen if you have multiple constructor with @Autowired
annotation. Spring won’t be able to decide which constructor to use. So, Spring will throw a BeanCreationException
.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vehicle' |
So you see, only a single constructor can exist with @Autowired
annotation. However we can set the required attribute as false for all the constructors and only in that case we can keep multiple constructor with @Autowired
annotation.
@Service
public class Vehicle {
@Autowired(required = false)
public Vehicle() {}
@Autowired(required = false)
public Vehicle(Engine engine) {
this.engine = engine;
}
}
The default value of the required attribute is true. So @Autowired(required = true)
is the same as @Autowired
. Now the question is, how will spring decide which constructor to use? In this special case Spring will use a constructor with the largest parameter. So in our example, it would be –
@Autowired(required = false)
public Vehicle(Engine engine) {
this.engine = engine;
}
One major benefit of constructor injection is it helps us to create immutable objects. In our example, all we have to do is make the engine field final
. Of course we cannot do that with field or setter injection.
Injection priority
Suppose we are using all three injection types in our class. In which order will the injection happen? Spring will follow below order –
- First the constructor injection will happen.
- Then field injection will happen.
- At the end setter injection will happen.
Injection type comparison
Let’s compare the three injection types –
Criteria | Field Injection | Setter Injection | Constructor Injection |
---|---|---|---|
Readability | Best | Worst | Better |
Immutability | Doesn’t support | Doesn’t support | Support Immutability |
Priority | Second | third | First |
Annotation count | Multiple fields can be annotated with @Autowired annotation. | Multiple setters can be annotated with @Autowired annotation. | Only one constructor can be annotated with @Autowired. |
That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.