Blog About Contact

Guice / Java IoC best practices - using annotations for configuration

Published Wed, 29 Sep 2010

18 months ago I introduced Guice 2.0 into my workplace development team.

So far it has been doing a good job of making inversion of control / dependency injection approachable, light-weight and helpful for the whole team.

Google Juice - http://www.flickr.com/photos/johannes-p-osterhoff/4775162612/sizes/m/in/photostream/

The biggest issue we've had with Guice has been unexpected bugs relating to object scoping misunderstandings.

These are bugs that originate from a mistake on the part of the developer, either forgetting or misunderstanding the scope of objects being interacted with and subsequently accessing the objects outside of their intended scope, or accessing the objects in a scope that turns out to be more expensive than intended (e.g. an object intended to be a singleton, being accessed with no scope and re-instantiated over and over).

What we've found is that as a standard it is best to use Guice's annotation based configuration mechanism to configure objects in their "intended configuration", rather than using "Guice modules".

Let me give an example of the two mechanisms -

With module -

public interface MyService {
   void serviceOperation1();
   void serviceOperation2();
   void serviceOperation3();
}

public class MyServiceImpl implements MyService {
   @Inject
   public MyServiceImpl() {
   }
   public void serviceOperation1() {
      // ...
   }
   public void serviceOperation2() {
      // ...
   }
   public void serviceOperation3() {
      // ...
   }
}

public class MyServiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(MyService.class).to(MyServiceImpl.class).in(Scopes.SINGLETON);
    }
}

With annotations -

@ImplementedBy(MyServiceImpl.class)
public interface MyService {
   void serviceOperation1();
   void serviceOperation2();
   void serviceOperation3();
}

@Singleton
public class MyServiceImpl implements MyService {
   @Inject
   public MyServiceImpl() {
   }
   public void serviceOperation1() {
      // ...
   }
   public void serviceOperation2() {
      // ...
   }
   public void serviceOperation3() {
      // ...
   }
}

These two examples are functionally identical, the only difference is how the Guice injector gets bootstrapped.

The common argument for Guice modules over annotations is that the application configuration is kept separate from the code, and can be changed in one central location. On the face of it, that seems like a pretty good argument.

In practice though, few objects can be arbitrarily rescoped without affecting the assumptions on which they were built, particularly where they have dependencies on other objects which have implicit scope. The only way to guarantee everything works as intended is to use Provider<MyDependency> everywhere to make sure things can continue working even if you monkey with the scoping in some unexpected way.

To me that seems like code-pollution which has rapidly diminishing returns.

The argument for @annotation based configuration is simple - understanding the intended scope and default implementation of an interface / object is an important signal to a developer reading the code for the first time. The best place for that information is in the code where the developer is reading it.

And best of all, this isn't an either-or decision.

The annotation based configuration only specifies the default configuration of your Guice application. If you specify a different scoping or binding in a Guice module, it will override anything specified using annotations.

So with this approach you retain the flexibility (and explicit definition) provided by modules, but still solve the "developer usability" issue of in-your-face scoping/bindings.


About the Author

Richard Nichols is an Australian software engineer with a passion for making things.

Follow him on twitter or subscribe by RSS or email.

You might also enjoy reading -


Discuss / Comment

No one has commented yet.

Add a comment

  • {{e.error}}

Thanks for your comment!/

Required.
Valid email address required.
Required.
Posting message, please wait...