I’ve followed the basic setup instructions on the GWT-GIN tutorial page. I’m on Step #3 (Declare bindings) and am trying to figure out how to use GIN’s Binder API.
public class MyModule extends AbstractGinModule {
@Override
public void configure() {
// 1. Declare an instance of EventBus and make sure every
// injection request pulls back the same instance.
EventBus eventBus = new EventBus();
bind(EventBus.class).to??? // no toInstance() method!
// 2. Declare two instances of Fizz using different constructors,
// and allow the injection of either of them.
Fizz f1 = new Fizz(true, "oh yeah", null);
Fizz f2 = new Fizz();
bind(Fizz.class).to??? // no toInstance() AND don't know how to choose f1 or f2!
// 3. Declare a List of Buzz objects and allow them to be
// injectable.
List<Buzz> buzzes = new ArrayList<Buzz>();
configureBuzzes(buzzes); // adds configured Buzz objects to the list
bind(???).to(buzzes); // no toInstance() methods AND how to bind List<?>.class?!
// 4. Conditionally bind SomePlace to Place *only* when we want the default Place
// that 'historyHandler.handleCurrentHistory()' will go to when called onModuleLoad.
bind(Place.class).to(SomePlace.class); // forces me to only have SomePlace instances!
}
}
The four use cases above are what I’m struggling with. Respectively:
- How to reuse the same instance of
EventBusevery time a client requests it? - Building from #1 above, how to have 2+ different instances that can be injected under different scenarios?
- How to inject a
Listof anything? - Might be the same as #2 above, but how to bind 2+ Place subclasses to
Place.class?
Thanks in advance!
Good questions that help to shed light on how Guice itself works, and the distinctions between Guice and Gin. Gin is not quite the same as Guice – the
configure()method runs when generating your JavaScript, so that the compiler only bakes in the right set of types – otherwise your app could potentially contain the whole JRE! This is slightly cheating for Gin to do this, and once you understand this, GWT DI makes a little more sense.The basic idea is that the
configure()method is only supposed to deal with wiring – not creating instances. This provides the answer to 1), and part of the answer to 2). Actually writing code that will be used when the app is running (Providerobjects,@Providesmethods, and of course anything annotated with@Inject) needs to be the other way around – it will be compiled only into JS. This means that while you can define methods likeconfigureBuzzesin 3), you need to be careful only to ever call these from inside theconfigure()method – and never callconfigure()from regular app code.The answers for 2), 3), and 4) are mostly to do with how Guice itself generally works. The solution I provide for 1) also works in normal Guice, and I would go so far as to suggest this approach all the time – I find it tend to make more readable code if you don’t mix the wiring and the actual object building.
Don’t create the instances in your
configure()method, just do the bindings. You can set the binding to be a For examplecreates the instance, and scopes it to be a singleton – the default constructor will be used by default.
If you want to use a non-default constructor, there are several options. You could annotate the particular constructor with
@Inject, and provide some annotation for each value (more on that in a moment), or you could build a provider or@Providesmethod to create the instance. Again, you may want@Singletonto have this make sense, but that’ll depend on your use case (this will be another method in your GinModule):Next, how do you provide two different kinds of the same thing? How do you do this in Guice? And how would you expect your code that gets a
Fizzinjected to get the right one? It turns out these probably all have the same answer – you need to find a way to indicate which instance you want. They are all the same type, so that isn’t enough, but we can provide other hints, like an annotation on the injected field. Say our code that will needf1andf2looks like thisNow we have a way to tell the difference, and we need to bind them using those same annotations. Since we’re still assuming no
@Injecton theFizzconstructor, we can’t just do abind()call, so instead we’ll just add@Blueto the provides method:We can read this as “This method
ProvidesBlueFizz instances.” For@Red, since we have the default ctor, we can usebind():See https://code.google.com/p/google-guice/wiki/BindingAnnotations for more details on this.
Again, we can use
@Providesfor this, or create and bind aProvider<T>type. As we’ve already done several provider methods, lets try aProvider<List<Buzz>>:Then, bind the Provider to that List:
You’re exactly right in your summary – this is exactly the same as 2. I usually make a
@DefaultPlaceannotation (or just plain@Defaultso I can reuse it all over) to deal with this kind of case.