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.