[Java Spring FAQs] Field injection is not recommended and Injection guidelines in Java Spring
Field injection is not recommended and Injection guidelines
Injection types
There are three options(Constructor-base Injection, Setter-based Injection and Field-based Injection) for how dependencies can be injected into a bean:
Constructor-base Injection
1 | public class DI { |
Pros
-
Better testability. You do not need any mocking library or a Spring context in unit tests. You can create an object that you want to test with the new keyword. Such tests are always faster because they not rely on the reflection mechanism. (This question was asked 30 minutes later. If the author had used constructor injection it would have not appeared).
-
Immutability. Once the dependencies are set they cannot be changed.
-
Safer code. After execution of a constructor your object is ready to use as you can validate anything that was passed as a parameter. The object can be either ready or not, there is no state in-between. With field injection you an introduce intermediate step when the object is fragile.
-
Cleaner expression of mandatory dependencies. Field injection is ambiguous in this matter.
-
Makes developers think about the design. dit wrote about a constructor with 8 parameters, which actually is the sign of a bad design and the God object anti-pattern. It does not matter whether a class has 8 dependencies in its constructor or in fields, it is always wrong. People are more reluctant to add more dependencies to a constructor than via fields. It works as a signal to your brain that you should stop for a while and think about your code structure.
Cons
- More code (but modern IDEs alleviate the pain).
Setter-based Injection
1 | public class DI { |
Field-based Injection
1 | public class DI { |
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.
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
Constructor-based or setter-based DI?
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required
annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
References
[4] - https://blog.marcnuri.com/field-injection-is-not-recommended