Java Spring - Bean Inheritance and Dependency Injection
In this tutorial, we will dive deep into Bean Definition Inheritance and one of Spring’s most powerful tool, Dependency Injection.
Be sure to check out the previous article, Bean Scope and Post processors.
As stated in the previous articles in the series, Spring framework is an open source Java platform that provides MVC infrastructure support for developing robust Java applications very easily and very rapidly using recommended MVC pattern.
Bean Definition Inheritance
When we define a bean, we provide a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name etc.
A child bean definition inherits configuration data from a parent bean definition. The child definition can override some values, or add more, as required.
Spring Bean definition inheritance has nothing to do with Java class inheritance but the inheritance concept is same. You can define a parent bean definition as a template and other child beans can inherit required configuration from the parent bean.
When you use XML-based configuration metadata, you indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute.
Now, let’s consider an example:
Class MyBean {
attrib1
attrib2
attrib3
attrib4
}
Let’s say that bean1 just needs attrib1 and attrib2 whereas another, say bean2’s instance, needs all four the attributes.
Lets configure these two beans:
<bean id="bean1" class="MyBean">
<property name="attrib1" value="val1" />
<property name="attrib2" value="val2" />
</bean>
<bean id="bean2" parent="bean1">
<property name="attrib3" value="val3" />
<property name="attrib4" value="val4" />
</bean>
Note that bean2 just needed to configure attrib3 and attrib4. The other two attributes are inherited from bean1.
Dependency Injection
Every java based application has a few objects that work together to present what the end-user sees as a working application. When writing a complex Java application, application classes should be as independent as possible of other Java classes to increase the possibility to reuse these classes and to test them independently of other classes while doing unit testing. Dependency Injection (sometime called wiring) helps in gluing these classes together while at same time keeping them independent.
Inversion of Control is a generic term meaning rather than having the application call the methods in a framework, the framework calls implementations provided by the application.
Dependency Injection is a form of IoC, where implementations are passed into an object through constructors/setters/service look-ups, which the object will 'depend' on in order to behave correctly.
IoC without using DI, for example would be the Template pattern because the implementation can only be changed through sub-classing.
Injection Types
There are three ways to inject object dependencies into a class. Constructor, Setter (Method) and Field injection. Let’s quickly compare the code of the same dependencies injected by all the approaches.
Constructor
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
this.dependencyA = dependencyA;
this.dependencyB = dependencyB;
this.dependencyC = dependencyC;
}
Setter
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public void setDependencyA(DependencyA dependencyA) {
this.dependencyA = dependencyA;
}
@Autowired
public void setDependencyB(DependencyB dependencyB) {
this.dependencyB = dependencyB;
}
@Autowired
public void setDependencyC(DependencyC dependencyC) {
this.dependencyC = dependencyC;
}
Field
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
Clearly seen in code above, Field injection is pretty easy to look at as there is no boilerplate code involved. The code is easy to read and navigate. Your class can just focus on what’s 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 the 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. It’s no problem to adding six, ten or even a 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 the 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 to fields as this approach can scale indefinitely.
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 is lacking 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.
Field injection should be mostly avoided. As a replacement, you should use either constructors or methods to inject your dependencies. Both have its advantages and disadvantages and the usage depends on the situation. However, as those approaches can be mixed, it is not an either-or choice and you can combine both setter and constructor injection in one class. Constructors are more suitable for mandatory dependencies and when aiming for immutability. Setters are better for optional dependencies.
Conclusion
In this tutorial, we talked about Dependency Injection and its implementation. We compared the ways and concluded which method is the best, or if Field injection should be used at all!
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment