[Best practice] Use Constructor-Based Dependency Injection with Spring

Old habits die hard, but die they must

If you’ve been using Spring for a while, or copy-pasting some web tutorials or examples, you’ve probably put some @Autowired annotations on private fields. This does work, but it’s not the best way to do it.


Don’t use @Autowired, create a constructor that takes all required fields as parameters.

If your class needs several constructors, annotate the one Spring should use with @Autowired.

Spring Team’s Recommendation #

If you have a Ultimate license of IntelliJ IDEA, if you annotate a private field with @Autowired, a warning should appear. Expanding it, you should see the following:

Field injection is not recommended
Inspection info: Spring Team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.

Using Constructor-Based Dependency Injection #

Annotation-Based Configuration #

Imagine a HelloService that needs Spring to inject a HelloRepository and a property from the application.properties file. The way to do so is to add a constructor that will take these parameters instead of annotating the private fields.

 2public class HelloService {
 4    private HelloRepository repository;
 5    private String fromName;
 7    public HelloService(
 8        HelloRepository repository,
 9        @Value("${kp.hello.from.name}") String fromName
10    ) {
11        this.repository = repository;
12        this.fromName = fromName;
13    }
15    // Logic here

From Spring 4.3, the @Autowired can be omitted for classes with only one constructor. With previous versions or classes that have more than one constructor, the annotation will indicate Spring which constructor to use.

Behind the curtains, when Spring runs, it discovers the HelloService via the @Service annotation and know it must be loaded. Then it will discover that there is a constructor whose parameters it will try to supply. It will search in the context for those and will try to pass those. Expect an exception if one of those is not available.

XML-Based Configuration #

XML remains a valid way to configure Spring (I had a hard time quitting it, actually), and it still works for constructor-based injection. For instance:

 1<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 2  <property name="location" value="application.properties"/>
 5<!-- ... -->
 7<bean id="helloService" class="org.keyboardplaying.demo.HelloService">
 8    <constructor-arg index="0" ref="helloRepository"/>
 9    <constructor-arg index="1" value="${kp.hello.from.name}"/>

There, no discovery is left to Spring, as everything is explicitly configured.

Pros & Cons #

Pro: Testability #

What if we wrote our HelloService the “traditional” way?

 2public class HelloService {
 4    @Autowired
 5    private HelloRepository repository;
 7    @Value("${kp.hello.from.name}")
 8    private String fromName;
10    // Logic here

Now imagine you want to unit test this. First, you’d have no solution to set those fields except using the reflection API, which completely breaks encapsulation.

Another con of field injection would be that you can’t ensure the bean has been fully initialized, which may result in random NullPointerException as your code would try to call a dependency that would not have been set.

Pro: OOP Paradigm #

From the Object-Oriented Programming point of view, using constructor is more natural to initialize objects.

Pro/con: Verbosity #

Complex services may require many dependencies. Associated constructors can become quite cumbersome.

But, as Baeldung highlighted, this may entice you to be more cautious about the number of dependencies of your service. Maybe write more specialized components and benefit from intelligent composition.

My Going Further #

Making Beans Immutable #

Since all dependencies of a service are set at the time of instantiation, why not make them final? This prevents any other component to change it at runtime, due to a security intrusion or a programming mistake for instance.

Not Writing the Constructor #

I’ve not yet had time to write about Project Lombok, but it shares one great purpose with Spring Boot: avoid boilerplate code.

Why spend time writing constructors, or updating them when the dependencies of a component change? Lombok uses reflection to generate these at compilation time.

For instance, you could do the following:

3public class HelloService {
5    private HelloRepository repository;
7    // Logic here

Or, making the service immutable, we could obtain:

3public class HelloService {
5    private final HelloRepository repository;
7    // Logic here

You can have a look at Lombok’s documentation to see the difference between the @AllArgsConstructor and @RequiredArgsConstructor.

There is however one big restriction to this use of Lombok: this cannot be used for constructor that requires annotated parameters (such as parameters annotated with @Value). In my experience however, most Spring beans can be initialized this way.

Works with Quarkus, Too #

Quarkus’s simplified dependency injection, ArC, is not a full CDI implementation. However, it brings the possibility of using [constructor injection][quarkus-ark-constructor].

I recently took advantage of it, instead of using @Inject, which is actually quite similar to @Autowired.

Conclusion #

Having other thoughts worthy of note about the use of constructor-based dependency injection? Please, share with us.