Mastodon

Use Dependency Injection via Constructor

Yesterday, I changed all of our field-injected Spring-dependencies to constructor-injected. This

@Autowired
private MyOtherService myOtherService;

became this:

private MyOtherService myOtherService;
public MyService(MyOtherService myOtherService) {
    this.myOtherService = myOtherService;
}

Here are the reasons for this refactoring, completed by Jens Schauder and Oliver Gierke:

  1. Cyclic dependencies can be found. Right after modifying the first service, the Spring context of our application could not be started because of a warning of cyclic dependencies. I wondered why constructor-injected dependencies behave in another way than field-injected ones. Apparently, the former makes it possible to detect cyclic dependencies. It took me an hour to fix all of the found issues, but every single one could be solved by moving a couple of methods. After that, the design looked much better and reflected the business logic way more intuitive.
  2. Easier to test. Normally, field-injected dependencies don’t have setter-methods to prevent setting a dependency programmatically. However, that prevents you from setting a mock while writing tests. Solution: Constructor-injection.
  3. More “object orientation feeling”. Using a constructor to set dependencies is way more natural in Java than using reflection.
  4. Only valid state can be created. With field-injection, simply calling the default constructor will create an instance of the class, but without the needed dependencies. Providing a constructor with every dependency guarantees a valid state.
  5. Mandatory dependencies are communicated. From the view of a user of the class, there’s no doubt about the dependencies this class has, because they have to be provided in the constructor. Field-injection only provides hidden dependencies.

Changing every field-injected class in my project took me 30 minutes. IntelliJ IDEA (the only good IDE in the world :P ) has a nice quickfix to create a constructor with every dependency.

After having changed all the classes, it took me an hour or so to get all the now obvious circular dependencies right. Being able to see those was really great and I’m happy that I could get rid of them.

TL;DR

Use constructor-injected dependencies, not field-injected ones!