I couldn't find any reasonable answer here on SO so I hope it's not a duplicate. So why should I prefer setter or constructor injection over simple
@Inject MyBean bean;
I get the usage of the constructor injection if you need to do something with injected bean during your class initialization like
public void MyBean(@Inject OtherBean bean) { doSomeInit(bean); //I don't need to use @PostConstruct now }
but still, it's almost the same like @PostConstruct
method and I don't get setter injection at all, isn't it just a relic after Spring and other DI frameworks?
Constructor-based DI fixes the order in which the dependencies need to be injected. Setter-based DI helps us to inject the dependency only when it is required, as opposed to requiring it at construction time. Spring code generation library doesn't support constructor injection so it will not be able to create proxy.
Constructor injection makes code more robust. It allows us to create immutable objects, preventing NullPointerException s and other errors. You can find the code example on GitHub.
Use Setter injection when a number of dependencies are more or you need readability. Use Constructor Injection when Object must be created with all of its dependency.
At this related question there are some valid arguments why to use injection via constructor instead of injection via field. It boils down to the advantage that you can use initialization via constructor also in non-CDI environment i.e. Unit Test, without the need to add more complex logic.
Now let’s see the differences between Setter Dependency Injection (SDI) and Constructor Dependency Injection (CDI) Whereas CDI happens during the object creation by the container by passing dependencies as a parameter to the constructor.
Difference between constructor and setter injection 1 Partial dependency: can be injected using setter injection but it is not possible by constructor. 2 Overriding: Setter injection overrides the constructor injection. 3 Changes: We can easily change the value by setter injection.
Overriding: Setter injection overrides the constructor injection. If we use both constructor and setter injection, IOC container will use the setter injection. Changes: We can easily change the value by setter injection.
In case you are not in the position to use constructor injection, or for whatever other reasons, you prefer setter injection, @Required is the way to go. Annotation a property’s setter an registering the RequiredAnnotationBeanFactoryPostProcessor as a bean in your application context is all you need to do:
Constructor and property injection gives you the option to initialize the object even in a non CDI environment easily, e.g a unit test.
In a non-CDI environment you can still simply use the object by just passing the constructor arg.
OtherBean b = ....; new MyBean(b);
If you just use field injection you usually must use reflection to access the field, because fields are usually private.
If you use property injection you can also write code in the setter. E.g. validation code or you clear internal caches that hold values which are derived from the property that the setter modifies. What you want to do depends on your implementation needs.
Setter vs constructor injection
In object-oriented programming an object must be in a valid state after construction and every method invocation changes the state to another valid state.
For setter injection this means that you might require a more complex state handling, because an object should be in a valid state after construction, even if the setter has not been invoked yet. Thus the object must be in a valid state even if the property is not set. E.g. by using a default value or a null object.
If you have a dependency between the object's existence and the property, the property should either be a constructor argument. This will also make the code more clean, because if you use a constructor parameter you document that the dependency is necessary.
So instead of writing a class like this
public class CustomerDaoImpl implements CustomerDao { private DataSource dataSource; public Customer findById(String id){ checkDataSource(); Connection con = dataSource.getConnection(); ... return customer; } private void checkDataSource(){ if(this.dataSource == null){ throw new IllegalStateException("dataSource is not set"); } } public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } }
you should either use constructor injection
public class CustomerDaoImpl implements CustomerDao { private DataSource dataSource; public CustomerDaoImpl(DataSource dataSource){ if(dataSource == null){ throw new IllegalArgumentException("Parameter dataSource must not be null"); } this.dataSource = dataSource; } public Customer findById(String id) { Customer customer = null; // We can be sure that the dataSource is not null Connection con = dataSource.getConnection(); ... return customer; } }
My conclusion
PS: My blog The difference between pojos and java beans explains my conclusion in more detail.
EDIT
Spring also suggests to use constructor injection as I found in the spring documentation, section Setter-based Dependency Injection.
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.
Constructor injection is also a better way when you think about unit tests, because it is easier to call the constructor instead of setting private (@Autowired) fields.
When using CDI, there is no reason whatsoever to use constructor or setter injection. As noted in the question, you add a @PostConstruct
method for what would otherwise be done in a constructor.
Others may say that you need to use Reflection to inject fields in unit tests, but that is not the case; mocking libraries and other testing tools do that for you.
Finally, constructor injection allows fields to be final
, but this isn't really a disadvantage of @Inject
-annotated fields (which can't be final
). The presence of the annotation, combined with the absence of any code explicitly setting the field, should make it clear it is to be set by the container (or testing tool) only. In practice, no one will be re-assigning an injected field.
Constructor and setter injection made sense in the past, when developers usually had to manually instantiate and inject dependencies into a tested object. Nowadays, technology has evolved and field injection is a much better option.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With