Dependency injection types

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 –

  1. Field Injection
  2. Setter Injection
  3. 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 –

  1. First the constructor injection will happen.
  2. Then field injection will happen.
  3. 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.