What Exactly Is Field Injection and How to Avoid It

What exactly is Field Injection and how to avoid it?

Injection types

There are three options for how dependencies can be injected into a bean:

  1. Through a constructor
  2. Through setters or other methods
  3. Through reflection, directly into fields

You are using option 3. That is what is happening when you use @Autowired directly on your field.


Injection guidelines

A general guideline, which is recommended by Spring (see the sections on Constructor-based DI or Setter-based DI) is the following:

  • For mandatory dependencies or when aiming for immutability, use constructor injection
  • For optional or changeable dependencies, use setter injection
  • Avoid field injection in most cases

Field injection drawbacks

The reasons why field injection is frowned upon are as follows:

  • You cannot create immutable objects, as you can with constructor injection
  • Your classes have tight coupling with your DI container and cannot be used outside of it
  • Your classes cannot be instantiated (for example in unit tests) without reflection. You need the DI container to instantiate them, which makes your tests more like integration tests
  • Your real dependencies are hidden from the outside and are not reflected in your interface (either constructors or methods)
  • It is really easy to have like ten dependencies. If you were using constructor injection, you would have a constructor with ten arguments, which would signal that something is fishy. But you can add injected fields using field injection indefinitely. Having too many dependencies is a red flag that the class usually does more than one thing, and that it may violate the Single Responsibility Principle.

Conclusion

Depending on your needs, you should primarily use constructor injection or some mix of constructor and setter injection. Field injection has many drawbacks and should be avoided. The only advantage of field injection is that it is more convenient to write, which does not outweigh all the cons.


Further reading

I wrote a blog article about why field injection is usually not recommended: Field Dependency Injection Considered Harmful.

Internal working of field injection in spring and why is it not recommended to use

Spring has a concept of Bean Post Processors.

When spring builds a bean it applies registered bean post processors that help to "initialize" the bean.

So, there is org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor that handles autowiring.

Basically it works with an newly created object. Spring introspects the fields of the beans (by using reflection). The fields that have @Autowired is a subject for processing with this bean post processor. It finds the candidate for injection in the application context and actually injects the value.

Now given this information, its understandable why final fields cannot be autowired. Leave alone spring, In pure Java, final fields must be instantiated directly right during the declaration (final int i = 123) or in the constructor of the class. But the autowiring happens after constructor, so its impossible to autowire the final fields.

As for the unit testing, the private properties must be somehow configured from the test. But since they're encapsulated (yes, spring kind of breaks encapsulation in this case for its usage), its impossible to write a good test for the class that contains fields injection. That's is a reason to switch to constructor injection.

public class FieldInjection {
@Autowired
private A a;
}

VS.

public class ConstructorInjection {

private final A a;

// this can be generated by lombok, you don't have to put @Autowired on constructor in the case of single constructor, spring will use it to create a bean
public ConstructorInjection(A a) {
this.a = a;
}
}

Now the test for FieldInjection class is impossible:


public class FieldInjectionTest {

@Test
void test() {
FieldInjection underTest = new FieldInjection();
how do you know that you should instantiate A a. ????
}
}

However in the case of constructor injection its a trivial task:

public class ConstructorInjectionTest {

@Test
void test() {
A a = mock(A.class);
ConstructorInjection underTest = new ConstructorInjection(a);
// the dependencies must be supplied in the constructor
// otherwise its impossible to create an object under test
}
}

Constructor injection vs Field injection

I found only two disadvantages in the field injection.

  • Hard to inject mocks when the object is under test. (Can be resolved with @InjectMocks from Mockito)

  • Circle dependencies. If bean A depends on bean B and bean B needs bean A. If you have the constructor injection it easy to find it.

Why does Spring treat constructor injection differently than setter/field injection?

So when you used constructor injection all beans which are used in constructor must be created earlier. If you have circular dependencies then they can not be created because of this cirular dependencies and spring throw exception.

When you use setter/field injection injected element are set after creation of bean, so cirular depenecies are allowed.

BTW if you have circular dependecies try to redesign you application, because your code will be harder to maintain. It's one of reason why constructor injection should be preferred.

How to prevent database field injection?

It is a good practice to separate the model and DTO - DTO that you transfer between frontend & backend controller- because sometimes there can be a lot more that just one field to exclude or handle differently. Also it can be that that field is needed in some other context so it can not be excluded in model itself.

So while your controller now receives User model it should receive UserDTO that has only the needed allowed fields.

This of course makes things bit more difficult because you then need to map UserDTO between User. But luckily there are libraries to handle that also like ModelMapper.



Related Topics



Leave a reply



Submit