Sunday, 25 September 2011

Guice Custom Scopes

Guice custom scopes are, for some reason badly explained in almost all web documentation that I was able to find.  All implementations cover extra Guice concepts or muddy the code with concurrency handling while not describing the main concept which is surprisingly simple once realised.

A scope is a condition for reusing injected objects, instead of creating a new one each time.  For example, if a network connection was being injected then the scope would be while the network connection was open, reuse the object.  When it is closed, open and inject a new one.

The implementation for this is to basically provide a factory object,  implementing the Scope interface.  This has one method:

Provider scope(Key<T> key, Provider<T> unscoped)

The Key object defines the injected type, the unscoped Provider can be used to create a new instance.  Typically the returned Provider implementation will  create the required object using the unscoped Provider and keep a reference to it while in scope.  When the object is requested again the stored object is returned if still in scope.  This example usage above is how I needed to use custom scopes but it is flexible enough to do all kinds of weird things.

Here is the Scope implementation.  It is a bit more complex than necessary because for this example I wanted to allow objects to be created out of scope.

Here is out annotation which we will use on classes that can be stored within the scope.
The interface to inject:
The class that implements that interface to be injected.  Note that we have annotated this with our @BookScoped annotation.  The constructor will display new objects being created.
Now our Guice module.  The scope annotation is bound to an EnterableScope instance which we'll keep a hold of.
Finally the main class to demo the scope.
When the main class is run it outputs the following to the command line:

entering scope
created customscopes.Hobbit@12a54f9
exiting scope
created customscopes.Hobbit@30e280
created customscopes.Hobbit@16672d6
created customscopes.Hobbit@fd54d6
created customscopes.Hobbit@1ccb029

When within scope the four calls to #getInstance only create one instance, when not in scope a new instance is created each time.  That's a whole lot of code although it is all fairly simple.  From a maintainability viewpoint I do see some issues with this, scopes are so arcane to anyone not experienced with Guice that it would be quite easy to misuse a scope which could cause all kinds of issues with retained state and possibly memory leaks.  All I can suggest is document everything and get this over to the team.

No comments: