Introduction

When running a static code analysis tool or inspecting/analyzing your code from your IDE, you may have encountered the following warning regarding your @Autowired fields:

Field injection is not recommended

This post shows the different types of injections available in Spring and what are the recommended patterns to use each of them.

Injection types

Although current documentation for spring framework (5.0.3) only defines two major types of injection, in reality there are three;

Constructor-based dependency injection

Setter-based dependency injection

Field-based dependency injection

The latter is the one which the static code analysis tool complains about, but is regularly and extensively used.

You can even see this injection method on some of Spring guides although being discouraged in the documentation:

Constructor-based dependency injection

In constructor-based dependency injection, the class constructor is annotated with @Autowired and includes a variable number of arguments with the objects to be injected.

Constructor-based dependency injection

Java

1

2

3

4

5

6

7

8

9

10

11

@Component

publicclassConstructorBasedInjection{

privatefinalInjectedBean injectedBean;

@Autowired

publicConstructorBasedInjection(InjectedBean injectedBean){

this.injectedBean=injectedBean;

}

}

The main advantage of constructor-based injection is that you can declare your injected fields final, as they will be initiated during class instantiation. This is convenient for required dependencies.

Setter-based dependency injection

In setter-based dependency injection, setter methods are annotated with @Autowired. Spring container will call these setter methods once the Bean is instantiated using a no-argument constructor or a no-argument static factory method in order to inject the Bean’s dependencies.

Setter-based dependency injection

Java

1

2

3

4

5

6

7

8

9

10

11

@Component

publicclassConstructorBasedInjection{

privateInjectedBean injectedBean;

@Autowired

publicvoidsetInjectedBean(InjectedBean injectedBean){

this.injectedBean=injectedBean;

}

}

Field-based dependency injection

In field-based dependency injection, fields/properties are annotated with @Autowired. Spring container will set these fields once the class is instantiated.

Setter-based dependency injection

Java

1

2

3

4

5

6

7

@Component

publicclassConstructorBasedInjection{

@Autowired

privateInjectedBean injectedBean;

}

As you can see, this is the cleanest way to inject dependencies as it avoids adding boilerplate code and there is no need to declare a constructor for the class. The code looks nice, neat and concise but as the code inspector already hinted us, there are some drawbacks to this approach.

Field-based dependency injection drawbacks

Disallows immutable field declaration

Field-based dependency injection won’t work on fields that are declared final/immutable as this fields must be instantiated at class instantiation. The only way to declare immutable dependencies is by using constructor-based dependency injection.

Eases single responsibility principle violation

As you know, in object-oriented computer programming, the SOLID acronym defines five design principles that will make your code understandable, flexible and maintainable.

The S in SOLID stands for single responsibility principle, meaning that a class should only be responsible for a single part of the functionality of the software application and all its services should be aligned narrowly with that responsibility.

With field-based dependency injection, it’s really easy to have lots of dependencies in your class and everything will look just fine. If constructor-based dependency injection is used instead, as more dependencies are added to your class, the constructor grows bigger and bigger and code starts to smell, sending clear signals that something is wrong.

Having a constructor with more than ten arguments is a clear sign that the class has too many collaborators and that maybe is a good time to start splitting the class into smaller and more maintainable pieces.

So although field-injection is not directly responsible for breaking the single responsibility principle it surely enough helps by hiding signals that otherwise would be really clear.

Tightly coupled with dependency injection container

The main reason to use field-based injection is to avoid the boilerplate code for getters and setters or creating constructors for your class. In the end, this means that the only way these fields can be set are by Spring container instantiating the class and injecting them using reflection, otherwise the fields will remain null and your class will be broken/useless.

The dependency injection design pattern separates the creation of class dependencies from the class itself transferring this responsibility to a class injector allowing the program design to be loosely coupled and to follow the Single responsibility and Dependency inversion principles (again SOLID). So in the end the decoupling achieved for the class by autowiring its fields is lost by getting coupled again with the class injector (in this case Spring) making the class useless outside of a Spring container.

This means that if you want to use your class outside the application container, for example for unit testing, you are forced to use a Spring container to instantiate your class as there is no other possible way (but reflection) to set the autowired fields.

Hidden dependencies

When using a dependency injection pattern, affected classes should clearly expose these dependencies using a public interface either by exposing the the required dependencies in the constructor or the optional ones using methods (setters). When using field-based dependency injection, the class is inherently hiding this dependencies to the outside world.

Conclusion

We’ve seen that field-based injection should be avoided whenever possible due to its many drawbacks however elegant it may seem. The recommended approach is then to use constructor-based and setter-based dependency injection. Constructor-based injection is recommended for required dependencies allowing them to be immutable and preventing them to be null. Setter-based injection is recommended for optional dependencies.