Google Guice and Multibinding

Mischa Dasberg

Last week I started migrating an application that used Spring for DI to Google Guice when I stumbled on multibinding.

Since Google Guice 2.0 we can use Multibinding which allows us to bind multiple objects to a collection.
But the one thing I missed in the current release is the ability to bind objects with a specific annotation. So I thought, why not build it myself 🙂


So lets use the Multibinder example written in the Javadoc of Multibinder.

public class SnacksModule extends AbstractModule {
    protected void configure() {
     Multibinder multibinder = Multibinder.newSetBinder(binder(), ISnack.class);
      multibinder.addBinding().toInstance(new Twix());
      multibinder.addBinding().toProvider(SnickersProvider.class);
      multibinder.addBinding().to(Skittles.class);
    }
  }

With this binding, a Set<ISnack> can now be injected:

  class SnackMachine {
    @Inject
    public SnackMachine(Set snacks) { ... }
  }

This is nice if you have a couple of Snacks, but if you have many and you want your
module clean and simple, you probably want something like this

 multibind(ISnack.class).toAnnotatedClasses(Snack.class);

Where a Snack looks like this:

@Snack
public class Snickers implements ISnack { ... }

To make this possible, I created the following Module:

public abstract class AbstractMultibindModule extends AbstractModule {

    public  AdvancedMultibinder multibind(Class clazz) {
        return new AdvancedMultibinder(clazz);
    }

    public class AdvancedMultibinder {
        private Multibinder multibinder;
        private Class clazz;

        private AdvancedMultibinder(Class clazz) {
            this.clazz = clazz;
            multibinder = Multibinder.newSetBinder(binder(), clazz);
        }

        public Multibinder toAnnotatedClasses(Class bindingAnnotation) {
            return toAnnotatedClasses(bindingAnnotation, clazz.getPackage().getName());
        }

        public Multibinder toAnnotatedClasses(Class bindingAnnotation, String basePackageName) {
            for (Class clazz : AnnotationUtil.getAnnotatedClasses(bindingAnnotation, basePackageName)) {
                multibinder.addBinding().to(clazz);
            }
            return multibinder;
        }

        public LinkedBindingBuilder addBinding() {
            return multibinder.addBinding();
        }
    }
}

As you can see the multibind method returns an AdvancedMultibinder. We cannot extend Multibinder because it has a private Constructor, so instead I use the multibinder internally and call methods on it to add bindings.

The AdvancedMultibinder has a method toAnnotatedClasses, which takes the annotation class as a parameter and adds a binding for each annotated class.

Now we can do the following.

  
     AdvancedMultibinder multibinder = multibind(ISnack.class);
     multibinder.toAnnotatedClasses(Snack.class);

     // Add a non annotated class to the bind.
     multibinder.addBinding().to(Skittles.class);

     bind(Machine.class).to(SnackMachine.class);

I have attached the example project here so you can look into the code yourself.

Comments (7)

  1. Jesse Wilson - Reply

    December 21, 2009 at 6:31 pm

    Neato!

  2. niels - Reply

    December 24, 2009 at 10:19 am

    >> @Snack
    >> public class Snickers implements ISnack

    I don't know anything about Guice, but it seems rather redundant. Isn't it possible to specify something similar to:
    AdvancedMultibinder multibinder = multibind(ISnack.class);
    multibinder.toImplementingClasses(ISnack.class);

    or even:
    AdvancedMultibinder multibinder = multibind(ISnack.class);
    multibinder.bindAllImplementingClasses();

    Niels

  3. Sakuraba - Reply

    December 24, 2009 at 10:29 am

    I wish those were available in GIN. Could definetly use those!

  4. iwein - Reply

    December 26, 2009 at 9:25 am

    That's pretty nice. I have to agree with niels though, the parametrization of the collection doesn't stack up against the redundancy of implementing the interface and annotate. But that's easily fixed I think.

  5. Mischa Dasberg - Reply

    December 26, 2009 at 1:16 pm

    @niels and Iwein,

    As you can see in the example I bind all Snacks with the Snack annotation. (maybe I should have called the annotation DefaultSnack)

    multibinder.toAnnotatedClasses(Snack.class);
    // Add a non annotated class to the bind.
    multibinder.addBinding().to(Skittles.class);

    And I add Skittles to my personal snackmachine which doesn't have the annotation.
    The downside of the toImplementingClasses would be that I can't exclude any implementing class.

  6. Erik Jan - Reply

    January 13, 2010 at 9:33 am

    Great blog, I've one question why would you want to migrate from spring to guice?

  7. Mischa Dasberg - Reply

    January 13, 2010 at 3:00 pm

    @Erik, why not? Both Spring and Guice work great, but Guice makes your code cleaner and the setup is easier and more readable.

Add a Comment