|
GinTutorial
Using GIN to create a GWT widget
Featured This tutorial will show how a simple GWT widget can be constructed with GIN. You should be already familiar with Guice - if not, then please read the Guice User Guide for an introductory overview of Guice concepts.
IntroductionIn regular Guice, you would use: // Does NOT work in GWT code! MyWidgetMainPanel mainPanel = injector.getInstance(MyWidgetMainPanel.class); However, as the comment points out that won't work in GWT:
Fortunately in GWT there is a different idiom which accomplishes the same purpose. Using GinStep 1. Inheriting the GIN module<module> ... <inherits name="com.google.gwt.inject.Inject"/> ... </module> Step 2. Defining the GinjectorDeclare an interface with methods that return the desired types: public interface MyWidgetGinjector extends Ginjector {
MyWidgetMainPanel getMainPanel();
}Experienced GWT users will notice the similarity to the way GWT image bundles and messages are done: You simply create a method for each object type you want to create, and the an implementation of the interface gets generated for you at compile time. Note that you only need to create injector methods for classes that you would directly access in your top-level initialization code, such as the UI classes to install in your RootPanel. You don't need to create injector methods for lower-level classes that will be automatically injected. So for example, if Class A uses class B which uses class C, you only need to create an injector method for A, as the other classes B and C will automatically be injected into A. In other words, injector methods provide a bridge between the Guice and non-Guice world. Step 3. Declare bindingsThe next step is to bind the various classes and providers using a Guice module. The module class looks almost exactly like it would in regular Guice (We use the GinModule and AbstractGinModule instead of Module and AbstractModule.) Here's an example module: public class MyWidgetClientModule extends AbstractGinModule {
protected void configure() {
bind(MyWidgetMainPanel.class).in(Singleton.class);
bind(MyRemoteService.class).toProvider(MyRemoteServiceProvider.class);
}
}Note that if GIN can't find a binding for a class, it falls back to calling GWT.create() on that class. What this means that image bundles and translated messages will just magically work. We emulate most of Guice's types but not modules, because:
For compatibility with regular Guice we provide a GinModuleAdapter class, which makes your GinModule available as a Module. Step 4. Associating the module with the injectorAdd the GinModules annotation to your Ginjector, specifying the module(s) needed to configure the application. @GinModules(MyWidgetClientModule.class)
public interface MyWidgetGinjector extends Ginjector {
MyWidgetMainPanel getMainPanel();
}Current project layout: MyWidgetProject/
client/
MyWidget.java
MyWidgetGinjector.java
MyWidgetMainPanel.java
MyWidgetClientModule.java
public/
server/Step 5. Creating the GinjectorTo create the injector instance, use the standard GWT.create() call. This can be done during static initialization: public class MyWidget implements EntryPoint {
private final MyWidgetGinjector injector = GWT.create(MyWidgetGinjector.class);
public void onModuleLoad() {
MyWidgetMainPanel mainPanel = injector.getMainPanel();
RootPanel.get().add(mainPanel);
}
}CompilationYour project should now be ready to be compiled. Since Gin uses Guice at compile-time and Guice operates on compiled java classes, you'll have to provide the compiled java classes to the gwt compiler. Example from an ant build file: <target name="javac" description="Compiles Java types needed during GWT compilation">
<mkdir dir="war/WEB-INF/classes"/>
<javac srcdir="src" destdir="war/WEB-INF/classes">
<classpath refid="project.libs"/>
</javac>
</target>
<target name="gwtc" depends="javac" description="GWT Web Compilation">
<java classname="com.google.gwt.dev.Compiler">
<classpath>
<pathelement location="src"/>
<!-- Note the reference to the compiled java classes -->
<pathelement location="war/WEB-INF/classes"/>
<path refid="project.libs"/>
</classpath>
<arg value="com.example.myapp.MyApp"/>
</java>
</target>Full ExampleYou can find an example for Gin project layout and compilation in our samples collection. Gin "Magic"Gin tries to make injection painless and remove as much boilerplate from your code as possible. To do that the generated code includes some magic behind the scenes which is explained here. Deferred BindingOne way Gin optimizes code is by automating GWT deferred binding. So if you inject an interface or class1 bound through deferred binding (but not through a Guice/Gin binding), Gin will internally call GWT.create on it and inject the result. One example are GWT messages and constants (used for i18n purposes): public interface MyConstants extends Constants {
String myWords();
}
public class MyWidget {
@Inject
public MyWidget(MyConstants myconstants) {
// The injected constants object will be fully initialized -
// GWT.create has been called on it and no further work is necessary.
}
}Note: Gin will not bind the instances created through GWT.create in singleton scope. That should not cause unnecessary overhead though, since deferred binding generators usually implement singleton patterns in their generated code. 1: Gin creates all instances through GWT.create (instead of new) for which it is legal: interfaces and classes with default constructors (whether they have an @Inject annotation or not). Remote ServicesThe RemoteService magic Gin performs is an extension of the deferred binding optimization explained above. Every time Gin is asked to inject an asynchronous remote service, it will inject an instance retrieved through calling GWT.create on its regular remote service: public interface MyRemoteService extends RemoteService { ... }
public interface MyRemoteServiceAsync { ... }
public class MyWidget {
@Inject
public MyWidget(MyRemoteServiceAsync service) {
// The 'service' will be created by calling GWT.create(MyRemoteService.class)
// prior to injection.
}
}
|
Gin also performs some magic when dealing with async remote services (GWT-RPC) and actually calls GWT.create(MyRemoteSyncService.class) if the Ginjector defines a method returning MyRemoteSyncServiceAsync.
Hi, I'm new to GIN but have a few questions 1. For the Remote Services example do you still need to define this in a module somewhere or is the point that you can just annotate with @Inject and it will work. 2. When using MVP, where is the best place to use dependency injection. Should I use it to create my presenters, so I can do injector.getPresenter(); 3. Is it possible to inject simple objects such as strings.
1. Nope, nothing to do, just put your remote service as a parameter on an @Inject constructor. 2. I've seen lots of debate on this issue. 3. Yes, use binding annotations. (http://code.google.com/p/google-gin/source/browse/trunk/samples/HigherLower/src/com/google/gwt/gin/higherlower/client/gin/HigherOrLowerModule.java)
I am new to Gin and using GWT 2.2, I added all the dependencies including guice-assistedinject-snapshot.jar, but I get this error on compile:
Just make sure you do not bind any type called "Cache". You will errors/exceptions, but it will just not work... silently!!
SVR, I'm getting that same error.
I was going through the sample HigherLower? and encountered this line
in com.google.gwt.gin.higherlower.client.gin.DeckProvider?. May be a dumb question but I would really like to know how I can implement a generator here.does gin provide autowiring by type?