I’m a nobody, nobody is perfect, therefore I’m perfect.

Java Dependency Injection and a Useless Annotation

I’ve stepped into the Guice territory rather recently — coming from the Spring framework side of things — and I guess I had so far a similar love/hate experience as with Spring. I rely mostly on the javax.annotation standards anyway so to a certain degree whether it’s Spring or Guice I guess doesn’t make that much difference to me. However, recently I have uncovered a rather big mess: the @PreDestroy annotation — and this seems to be particularly bad with Guice!

I’ve used before @PostConstruct and in one particular case I thought it’s time to bring in its counterpart @PreDestroy. So I’ve set off to write my services to make use of it — and nothing happened! I mean really, my @PreDestroy‘s never got called. Not once. Ever!

Simply using just Google Guice — no other frameworks. Just create one of their injectors and see how @PostConstruct and @PreDestroy behave

I’ve thrown in Netflix Governator too — after my findings in the first step — and check the same things.

I got some results … but nothing I expected to be honest! So let’s look at each case individually.

Google Guice Only

Having operated for a while in Netflix Governator environments, I completely didn’t realise that actually Google Guice does NOT support @PostCreate and @PreDestroy — it doesn’t have any lifecycle annotation support at all! I found this shocking, but it’s true.

To prove it (to myself more than anything else) I set up this code (plus some scaffolding around it which you can find in the Github repo): a simple StartApp class which gets injected other “services” classes such as MyService (ingenious name, I know!). Each one of these classes implements a constructor, a finalize() method, a @PostConstruct and a @PreDestroy. All these methods do is just print on the console when they are invoked — so I can:

first of all see that they are called

secondly, check the order of invocation.

The reason behind it is that @PostConstruct annotation is pretty self-explanatory: it executes after the object got constructed (so after the constructor kicked in and initialized our object). Why is it needed you might ask? Because there are DI frameworks which allow you for instance to mix @Inject annotations with other annotations that only kick in after your instance got created — so in such cases you won’t have access to some of the member data until after your instance has been created. So you need another checkpoint right before you finally start putting the instance created to (good) use. Even the JavaDoc for this annotation sums this up by stating:

The PreDestroy annotation is used on methods as a callback notification to signal that the instance is in the process of being removed by the container. The method annotated with PreDestroy is typically used to release resources that it has been holding.

OK, so a callback before destruction — but when is that? Because the second part of it sounds very very closely to finalize()! When does the container drop an instance? Is it at garbage collection time? Before? Because of the garbage collection? Or just triggered by other conditions? No explanation to that but the same JavaDoc goes to enforce this:

This annotation MUST be supported by all container managed objects that support PostConstruct except the application client container in Java EE 5.

OK, so that means if I use @PostConstruct I should be also able to use @PreDestroy — so that’s why (and how!) I set up to write this simple code: on the assumption that once I get @PostConstruct to be called (and I have definitely seen and used this with Guice and Governator) then I should see @PreDestroy kick in too! Well, little did I realise how wrong I was 🙁

Getting back to my simple code, here is my simple StartApp class which is injected all the other instances — nothing fancy as you can see:

As you can see, I’m creating the Guice Injector, requesting my StartApp instance and invoke the main method — then I go and explicitly nullify it, suggest GC (so the object get finalized), and since the GC is just a suggestion, I even give it a few seconds to allow GC to kick in and clean up.

This is what I expected the code to print out:

starting up

injection startup

Retrieving StartApp instance

MyService constructor

MyService PostConstruct

StartApp constructor + finished

StartApp PostConstruct

Startapp instance created

… all the stuff about running the StartApp run() method

StartApp PreDestroy

StartApp finalize()

MyService PreDestroy

MyService finalize()

This is based on my assumption that somehow the PreDestroy will be triggered by garbage collection.

As it turns out, Google Guice on its own does not provide any lifecycle management support — I found this post from ages ago mentioning it: https://groups.google.com/forum/#!topic/google-guice/0M3molmK4IU ; as a suggestion to the Guice peeps, it would be good if their (otherwise excellent) docco states this clearly too. Since they are referencing the java.annotation package, one is lead to believe they support this too — I definitely was.

OK, so note to all Guice users: unless you add other libraries in your app you do not get any lifecycle support such as @PostCreate and @PreDestroy in your app!

Using Netflix Governator

Having learned that, I have turned my attention back to the Netflix Governator — a framework we use extensively in Netflix (d’er!). And one which I know damn well it has support for @PostConstruct as I have used it myself tons of time!

And based on the above JavaDoc, this means it should support @PreDestroy too, right? So then I should be able to find out when does this damn code kick in.

Let’s keep my StartApp and MyService classes the same and only change the way we create the injector — that’s the beauty of DI, right? I can switch provider with no code changes! — such that we create a Governator lifecycle management-enabled injector. Sadly the Governator wiki is a bit of out date, their example of creating an injector is outdated — I have used instead this code:

(The PreDestroyTestModule is just an empty Google Guice module by the way.)

Now I should see something similar to my expectations above — or if my instances are being dropped by the container for other reasons then I should see these way before the finalize() kicking in. Or perhaps I will see it in when the JVM exits and the DI container cleans up after itself. Either way, I was excited to finally see @PreDestroy in action … and I got this:

Notice that this looks much better than the previous one: I’m actually seeing @PostConstruct methods kicking in, and it is in the order I expected it, thus confirming my understanding of this annotation. But no sign of @PreDestroy! Nothing! It gets worse — you can in fact play with the above code and do all sorts of things, forcing injector shutdown’s and so on to no avail. It seems there is no @PreDestroy support built in this either.

Conclusion

At this point I give up — I believe the @PreDestroy annotation is somewhere in between a myth and a useless annotation as I don’t think it is supported by that many frameworks (or at least not supported by these 2 major ones). I blame this on Oracle and their poor specification — if you look at the JavaDoc again, there is no requirements as to when this method should be evaluated and as such any platform can claim they are implementing this but just choose to say invoke the @PreDestroy‘s when the JVM shuts down (or in fact any other weird condition that they can think of). The JDK spec does a good job of trying to enforce that both @PostConstruct and @PreDestroy are implemented together but failing to strongly specify when and how should both be implemented renders one of these useless. It’s a half-arsed specification and it looks like the Oracle folks lost the steam by the time they got to @PreDestroy. Sad … and useless!

My take from this is @PreDestroy is a no go — it might get implemented by some DI frameworks but because of this probability anyone using it should be aware that they are going down the vendor-lockin path. Which, again, to my original point, means that the @PreDestroy annotation is now useless.