Defensive Progamming Against Unfulfilled Dependencies
Dependency Injection offers tremendous opportunities to write objects that are both simpler and more supple. Ease of testing, reusibility, more literate code – Dependency Injection is here to stay.
But when we free objects to receive rather than lookup their dependencies, we open the possibility that the dependency injector will drop the ball, fail to fulfill its part of the contract: not supply the dependencies the objects crave.
What's a pojo to do?
Constructor injection and guarantees of state
One appproach is to use "constructor injection" – receiving dependencies as constructor arguments – and checking those arguments at construction before storing them into final instance fields.
Advantages of this approach include the very attractive guarantee the object can provide: constructor injection and argument checking, ideally, results in objects that cannot be instantiated without providing their dependencies. Such an implementation eagerly detects and reports on this problem, instead of waiting for it to bite later. The most important advantage of this approach is that it is 100% simple pojo: Java code outside of Spring or any other container instantiating YourObject gets the same dependency checking and state defensiveness benefits available within the container without having to provide special container services, invoking special methods, etc. You can't help but benefit from the checking.
Downsides of this approach include the complexity of having argumented constructors, the reduced elegance and less self-documenting-ness of multiple constructor arguments (have you gotten them in the right order?) vs. setter injection. The Spring bean wiring XML for constructor injection is more awkward than that for setter injection.
Automatic Spring dependency checking
Spring is capable of examining an object's setter methods expressing opportunities to inject dependencies and determining whether those opportunities are being realized (whether Spring is actually injecting, by explicit declaration or by autowiring). This can be set to no checking (default), checking for injection of primitives, checking for injection of collections, or checking all properties.
The advantage of this approach is that it doesn't require touching the Java code of the object being provisioned to add the checking.
One disadvantage of this approach is that, just as it is possible to forget to inject needed properties, it's possible to forget to configure or to misconfigure dependency injection checking. The major disadvantage, tho, is that this approach moves dependency checking purely into declarative configuration, leaving the object as pojo continuingly defenseless.
Spring afterPropertiesSet()
Spring will also examine any object it is instantiating to see if it implemements the Spring bean lifecycle interface which declares the method afterPropertiesSet(). Where this interface is implemented, after Spring completes its injection of dependencies, it calls afterPropertiesSet(), giving the object an opportunity to review its provisioned properties. While some checking can and should be implemented in particular setter methods (invocation with null or otherwise illegal arguments), afterPropertiesSet() lends itself to the kind of checking that cannot be accomplished in setter methods (requiring that particular dependencies were injected, such as requiring that dependencies be provided and requiring relationships between dependencies.
The advantage of this approach is that it isn't possible to forget to declare the checking in Spring bean configuration and implementing the afterPropertiesSet() method in Java provides an opportunity to document the object's dependency injection expectations.
Disadvantages of this approach surround its coupling the object implementation to Spring. The object is importing and implementing a Spring interface and now requires the Spring API .jar to follow it along wherever it goes. Implementing this Spring interface makes an object less pojo-like, and more a Spring bean. Using the object outside the Spring container becomes more complicated, with the programmer needing to remember to invoke afterPropertiesSet() after configuring the object to check dependencies or to lose the advantages of this checking. In the case where afterPropertiesSet() is being used not just for optional, supplemental checking but to fulfill the contract of the object itself (set defaults were the object API states that a particular dependency is optional, for example), forgetting to invoke the afterPropertiesSet() lifecycle method can make the object fail unexpectedly later.
Arbitrary lifecycle methods
An object might invent its own afterPropertiesSet() equivalent method.
Spring offers the capability of invoking an arbitrary object method after it has completed provisioning the object. This requires declarative configuration in the Spring bean wiring XML.
Advantages of this approach is allowing afterPropertiesSet() checking without becoming binary dependent upon Spring.
Disadvantages of this approach is that it moves actually turning on dependency checking back into optional declarative configuration. At the same time I forget to provide some particular dependency, I can also forget to invoke the lifecycle method. This approach also introduces a documentation issue: the object developer needs to be careful to document clearly the object's lifecycle method expectations. Finally, adding lifecycle methods makes an object less pojo-like. Even with this documentation, it is easy for a developer using this object outside the context of the Spring container to forget to invoke the lifecycle method, losing the advantages of dependency checking.