I read in some posts about Spring MVC
and Portlets
that field injection is not recommended. Cause i'm trying to get a So i asked myself if i'm using field injection and i can't answer it. As i understand it field injection is if you inject a Bean
into a attribute with @Autowired
like this:
CartController.java:
...
@Autowired
private Cart cart;
...
BookshopConfiguartion.java:
@Configuration
public class BookShopConfiguration {
@Bean
public Cart cart(){
return new Cart();
}
//more configuration
My Cart.java
is used to store and provide information about the books in the cart.
During my research i read about constructor injection:
MyComponent.java:
...
public class MyComponent{
private Cart cart;
@Autowired
public MyComponent(Cart cart){
this.cart = cart;
}
...
What are the advantages and the disadvantages of both of these types of injections?
EDIT 1: As this question is marked as duplicate of this question i checked it. Cause there aren't any code examples neither in the question nor in the answers it's not clear to me if i'm correct with my guess which injection type i'm using.
Injection types
There are three options for how dependencies can be injected into a bean:
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:
Field injection drawbacks
The reasons why field injection is frowned upon are as follows:
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.
What is wrong? As you can see, the Field variant looks very nice. It is very short, concise, there is no boilerplate code. The code is easy to read and navigate. Your class can just focus on the important and is not polluted by DI boilerplate. You just put the @Autowired annotation above the fields and that's it. No special constructors or setters just for DI container to provide your dependencies. Java is very verbose as is, so every opportunity to make your code shorter is welcome, right?
Single Responsibility Principle Violation
It is very easy to add new dependencies. Maybe too easy. There is no problem in adding six, ten or even dozen dependencies. When you are using constructors for DI, after a certain point, the number of constructor params becomes too high and it is immediately obvious that something is wrong. Having too many dependencies usually means that the class has too many responsibilities. That may be a violation of Single Responsibility Principle and separation of concerns and is a good indicator, that the class requires further inspection and possible refactoring. There is no such red flag when injecting directly into fields as this approach can scale indefinitely.
Dependency Hiding
Using DI container means that the class is no longer responsible for managing its own dependencies. Responsibility for obtaining the dependencies is extracted from the class. Someone other is now responsible for providing the dependencies - DI container or manually assigning them in tests. When the class is no longer responsible for obtaining its dependencies, it should clearly communicate them using public interface - methods or constructors. This way it is clear what the class requires and also whether it is optional (setters) or mandatory (constructors).
DI Container Coupling
One of the core ideas of the DI frameworks is that the managed class should have no dependency on the DI container used. In other words, it should be just a plain POJO, which can be instantiated independently, provided you pass it all the required dependencies. This way you can instantiate it in a unit test without starting the DI container and test it separately (with a container that would be more of integration test). If there is no container coupling, you can use the class either as managed or non-managed or even switch to a new DI framework.
However, when injecting directly into fields, you provide no direct way of instantiating the class with all its required dependencies. That means:
There is a way (by calling the default constructor) to create an object using new in a state when it lacks some of its mandatory collaborators and usage will result in the
NullPointerException
. Such a class cannot be reused outside DI containers (tests, other modules) as there is no way except reflection to provide it with its required dependencies. Immutability Unlike constructor, field injection cannot be used to assign dependencies to final fields effectively rendering your objects mutable.This is one of the never-ending discussions in software development, but major influencers in the industry are getting more opinionated about the topic and started to suggest constructor injection as the better option.
Constructor injection
Pros:
Cons:
Basically, the field injection is the opposite.
Matter of taste. It is your decision.
But I can explain, why I never use constructor injection.
I don't want to implement a constructor for all my
@Service
,@Repository
and@Controller
beans. I mean, there are about 40-50 beans or more. Every time if I add a new field I would have to extend the constructor. No. I don't want it and I don't have to.What if your Bean (Service or Controller) requires a lot of other beans to be injected? A constructor with 8 parameters is very ugly.
If I'm using CDI, constructor does not concern me.
EDIT: Vojtech Ruzicka said:
Yes. Theory and reality. Here is en example:
DashboardController
mapped to single path*:8080/dashboard
.My
DashboardController
collects a lot of informations from other services to display them in a dashboard / system overview page. I need this single controller. So I have to secure only this one path (basic auth or user role filter).