@Autowired and Optional Dependencies
Are your injected dependencies required or optional? What about all the permutations of possibilities?
Join the DZone community and get the full member experience.
Join For Free@Autowired(required=false)
@Autowired annotation by default has an element "required" set to true, which means that the annotated dependency is required. However, we can turn off the default behavior and make the dependency optional as follows:@Autowired(required=false)
private Dependency dependency;
It can be useful, and since not all dependencies are always required, introducing this possibility was reasonable.So what’s the problem with dependencies annotated in this way? Let’s look at the code:
class SomeClass {
@Autowired private DependencyA dependencyA;
@Autowired private DependencyB dependencyB;
@Autowired(required=false)
private DependencyC dependencyC;
@Autowired(required=false)
private DependencyD dependencyD;
}
We can create an instance of the SomeClass with following dependencies (all combinations are allowed):- DependencyA, DependencyB
- DependencyA, DependencyB, DependencyC
- DependencyA, DependencyB, DependencyD
- DependencyA, DependencyB, DependencyC, DependencyD
Optional Dependencies - Do it Right!
If we are considering an example presented in the previous paragraph, there are two possible answers to the following question:- All combinations are possible.
- Only a subset of the combinations is possible.
What about the second point - a subset of the combinations? Let’s assume that only creating an object in either state 1 or state 4 is valid. Leaving the code as it is can result in the wrong usage of the object. We are allowing for the creation an object in invalid states (2 and 3). What can we do about this?
In that case, I think that we should drop @Autowired annotation. For the sake of the code readability and design quality it would be better to use constructors instead:
class SomeClass {
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
private DependencyD dependencyD;
public SomeClass(DependencyA dependencyA, DependencyB dependencyB) {
this.dependencyA = dependencyA;
this.dependencyB = dependencyB;
}
public SomeClass(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC, DependencyD dependencyD) {
this(dependencyA, dependencyB);
this.dependencyC = dependencyC;
this.dependencyD = dependencyD;
}
}
With this code, you know everything that is needed. You know what dependencies are required to create a correct object.
Stay Aware!
The article is not intended to convince you that it is better to not use @Autowired(required=false). Its purpose is to make you aware of the cost that you have to pay.
You have to protect your design, you have to protect object invariants and you should not allow for the existence of the objects in an invalid state. Of course, you may add a documentation or comment, but if we already have a semantic provided by the language which allows us to make it without additional effort we should use it.
Published at DZone with permission of Sebastian Malaca, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments