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:
EventBus
every time a client requests it?List
of anything?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 (Provider
objects, @Provides
methods, 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 like configureBuzzes
in 3), you need to be careful only to ever call these from inside the configure()
method - and never call configure()
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 example
bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
creates 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 @Provides
method to create the instance. Again, you may want @Singleton
to have this make sense, but that'll depend on your use case (this will be another method in your GinModule):
@Provides
//@Singleton //optional, depends on your use case
public Fizz provideFirstFizz() {
return new Fizz(true, "oh yeah", null);
}
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 Fizz
injected 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 need f1
and f2
looks like this
@Inject
@Red// Make up your own annotation, or use some existing ones
private Fizz f1;
@Inject @Blue private f2;
Now we have a way to tell the difference, and we need to bind them using those same annotations. Since we're still assuming no @Inject
on the Fizz
constructor, we can't just do a bind()
call, so instead we'll just add @Blue
to the provides method:
@Provides
@Blue
//@Singleton //optional, depends on your use case
public Fizz provideFirstFizz() {
return new Fizz(true, "oh yeah", null);
}
We can read this as "This method Provides
Blue
Fizz instances." For @Red
, since we have the default ctor, we can use bind()
:
bind(Fizz.class).annotatedWith(Red.class);//... no need to specify type
//in this case, but might want
//singleton
See https://code.google.com/p/google-guice/wiki/BindingAnnotations for more details on this.
Again, we can use @Provides
for this, or create and bind a Provider<T>
type. As we've already done several provider methods, lets try a Provider<List<Buzz>>
:
public class BuzzListProvider implements Provider<List<Buzz>> {
public List<Buzz> get() {
List<Buzz> buzzes = new ArrayList<Buzz>();
// Configure them... This might call on a @Inject defined
// within this BuzzListProvider, on the ctor or a field, or
// just some code in this method.
return buzzes;
}
}
Then, bind the Provider to that List:
// cant say List<Buzz>.class, use TypeLiteral instead
bind(new TypeLiteral<List<Buzz>>(){})
.toProvider(BuzzListProvider.class);
// .in(Singleton.class); if the list needs to be only created once
You're exactly right in your summary - this is exactly the same as 2. I usually make a @DefaultPlace
annotation (or just plain @Default
so I can reuse it all over) to deal with this kind of case.