Field injection is not evil

I am a big fan of the work Oliver Gierke has done in Spring Data. It is a framework I use daily at work and it really is amazing. A while back Greg Turnquist had posted something on Twitter against field injection. So I asked what was wrong with field injection and he pointed me to this post that Oliver had written about it.

This post is going to be my attempt to argue against Oliver’s position on the subject. It may wind up an epic failure, but if nothing else I figured it would help me think through my thoughts and assumptions on the issue and see if I can make an argument in favor of it. First head over to Oliver’s post on the topic and read it before you continue on.

Argument 1: “It’s a null pointer begging to happen

In Oliver’s example he MyCollaborator into MyComponent with the @Inject tag that you would use in Spring or in CDI. Then his argument is that a person would instantiate MyComponent outside of the Container with MyComponent component = new MyComponent().

My first thought is, you are dealing with a container managed component if you have @Inject in it so if someone is standing up the component outside of the container to me that already says they are doing something wrong. Given that I don’t find this a compelling argument. If you are using the component in a framework as it is designed to be used the framework will tell you if it can’t wire up the component due to missing dependencies, whether your container is Spring or it is JavaEE.

I suppose one could imagine designing a component that could be used in either Spring or CDI or standalone without a framework, and Oliver’s constructor example could still work (assuming you don’t blow up on the missing @Inject annotation which I think could happen.) So in my opinion this argument isn’t really valid or a big concern as is someone is misusing your component I am not too concerned with them getting a null pointer in that scenario.

Let’s consider his benefits of Constructor Injection. The first case is you can only create your component by providing all the dependencies. This forces the user of the component to provide everything. While I consider this a valid argument, I think the point is somewhat moot since we are talking about designing a component that already has a lifecycle to it and that a framework will inject the dependency for.

His second benefit is that you communicate mandatory requirements publicly. I have to admit I find this his most compelling argument. I don’t have a counter argument to this.

His third argument is that you can then set those fields final. I have to admit I do like making everything final so that is also a compelling argument to me. But not enough to counter the negatives of it.

Let’s consider the argument that he tries to dispel:

An often faced argument I get is: “Constructors just get too verbose if I have 6 or 7 dependencies. With fields only, this is fine”. Awesome, you’ve effectively worked around a clear indicator that the code you write is doing way too much. An increase in the number of dependencies a type has should hurt, as it makes you think about whether you should split up the component into multiple ones.

I don’t buy his counter argument here. In the project that I am working on we have a bunch of service level business logic components. We have a thin service layer that we expose to the presentation tier, and then do all the heavy lifting of our business logic in these hidden service level components. Given the high volume of business logic code we have we compose the different operations with injected reusable business logic components. This is also a huge benefit when working with a large team you get less merge conflicts as the code is spread out across more files and when you are onboarding new employees you have a lot more smaller business logic classes that are easier to digest than some massive classes with all this business logic in it.

If I split those components up that is what we have already done which is why we have to inject them all over the place to reuse those business methods. I think when dealing with a non-trivial app that is extremely large, breaking things into fine grained components that are reusable in multiple places actually leads to needing to inject more fields and not less.

Argument 2: Testability

In this case Oliver argues that you have to be doing reflection magic in unit tests if you use field level injection. To which I respond if you are running a dependency injection framework why wouldn’t you be using a dependency injection unit test framework. And of course we do. The answer is Mockito. With Mockito you structure your unit test like you would structure your component. So to test my service I might have something like below:

@InjectMocks final MyService service = new MyService();

@Mock Component requiredComponent;

@Test public void myTest() {

when(requiredComponent.methodCall()).thenReturn(new MockedResponse);

final boolean result = service.methodIAmTesting();

assertTrue(result);

}

Now I have a unit test that is structured largely like my service. The testing framework injects the mocked dependencies into our service we are testing and we wire up the results on the mocks so we can test our service. Again this strikes me as very testable as now I am writing tests that look a lot like the way my services themselves look.

So that is my argument against. Basically I think the number of arguments to the constructor actually does get extremely unreasonable in many real world scenarios where you have a lot of finely defined business logic components that can be composed in many different ways to solve the project. You do end up with a lot more boiler plate, and even if you use Lombok to hide it then you have ugly and somewhat cryptic Annotations to tell Lombok to put the @Inject annotation on the constructor. I think if you are running a dependency injection framework, it isn’t reasonable for people to instantiate those objects outside of that framework, and likewise it is very reasonable to expect your unit test framework to also be dependency injection driven.

Let me know if I have persuaded anyone to my point or if you think I am completely wrong and Oliver is right about this, I would love to hear someone explain to me why my arguments are wrong and why Oliver has been right all along, or if there is a 3rd even better way what that way is.