I. L'intérêt▲
Avant de détailler comment mettre en place GIN, nous allons voir à quelles fins nous pouvons l'utiliser. Tout d'abord, ne cherchez pas l'intérêt de Gin du côté de la performance. Le code généré en utilisant Gin sera équivalent à un code développé de façon classique (il ne faut pas oublier que tout le code écrit en Java (côté client GWT) est transformé et optimisé par le compilateur GWT pour donner au final du code JavaScript).
L'intérêt principal réside dans la simplification du code et sa lisibilité. En utilisant l'injection de dépendance, vous n'avez plus besoin de garder des références static de partout pour accéder à vos services ou vos objets. Imaginez pouvoir accéder à votre EventBus ou votre Bundle d'images en ajoutant simplement l'annotation @Inject, et là, la magie opère.
Utiliser Gin va également vous permettre de mettre en place une politique précise d'instanciation de vos objets. Cette politique correspond au binding que vous déclarez dans la configuration du module Gin, nous verrons cela après.
Pour en revenir au côté simplification du code, l'injection automatique mais aussi l'instanciation automatique de tous les dictionnaires, les bundles d'images et les services RPC (Remote Procedure Call) est une facilité à laquelle on devient vite accro.
II. Mise en place▲
Maitenant, mettons un peu les mains dans le cambouis. La mise en place de Google Gin est extrêmement simple et bien documentée.
Avant tout, il faut importer le module GIN. Pour cela, nous allons le déclarer dans le fichier de configuration de notre application GWT à l'aide de la balise inherits :
<module>
...
<inherits
name
=
"com.google.gwt.inject.Inject"
/>
...
</module>
Ensuite, déclarer une interface qui hérite de Ginjector (prononcez “Gin-jector” et non “G-Injector”) qui doit définir des méthodes avec des types de retour correspondant aux types désirés. Seuls les objets que vous souhaitez récupérer depuis l'instance de l'injecteur doivent être ajoutés dans cette interface. Les objets injectés automatiquement via l'annotation @Inject par exemple, n'ont pas besoin d'être déclarés. En d'autres mots, comme le dit la documentation, “Les méthodes de l'injecteur fournissent un pont entre le monde Gin et le monde non-Gin”.
public
interface
ZenGinjector extends
Ginjector {
MainPanel getMainPanel
(
);
}
La prochaine étape est de déclarer le binding. Dans l'exemple suivant, nous allons binder la classe MainPanel en tant que Singleton tandis que la classe ZenRemoteService est bindée sur un Provider, ZenRemoteServiceProvider. Le binding définit comment seront instanciés les objets en question. Par exemple, lorsque vous utiliserez l'injecteur pour MainPanel, la même instance vous sera toujours renvoyée, puisque le scope est “Singleton”.
public
class
ZenClientModule extends
AbstractGinModule {
protected
void
configure
(
) {
bind
(
MainPanel.class
).in
(
Singleton.class
);
bind
(
ZenRemoteService.class
).toProvider
(
ZenRemoteServiceProvider.class
);
}
}
Il faut par la suite lier le module avec l'injecteur en utilisant l'annotation @GinModules.
@GinModules
(
ZenClientModule.class
)
public
interface
ZenGinjector extends
Ginjector {
MainPanel getMainPanel
(
);
}
Finalement, une fois que toutes les classes et interfaces pour définir l'injecteur sont prêtes, il ne vous reste plus qu'à instancier celui-ci, et pour cela rien de mieux que la bonne vieille méthode GWT.create(...) et ensuite récupérer les objets que l'on souhaite en appelant les méthodes définies sur l'injecteur.
public
class
ZenGinExample implements
EntryPoint {
private
final
ZenGinjector injector =
GWT.create
(
ZenGinjector.class
);
public
void
onModuleLoad
(
) {
MainPanel mainPanel =
injector.getMainPanel
(
);
RootPanel.get
(
).add
(
mainPanel);
}
}
Pour rappeler ce qui a été dit précédemment, vous n'avez besoin de l'injecteur que pour faire le pont Gin/Non-GIN, récupérer les objets dont vous aurez besoin via les méthodes de l'injecteur. Pour les autres objets, vous pouvez utiliser l'injection classique de Guice, comme dans l'exemple suivant, via annotations.
public
class
ZenWidget {
@Inject
public
ZenWidget
(
MyRemoteServiceAsync service) {
...
}
}
Il est intéressant de noter qu'il n'est pas nécessaire de déclarer de binding pour les objets qui sont créés via la méthode GWT.create(...), tels que les dictionnaires d'i18n, les bundles d'images ou de css ou encore les services RPC de GWT, car lorsque Gin ne trouve pas de binding, il utilise en fallback GWT.create(...)
III. Les différences avec Guice▲
La première différence entre Gin et Guice est le nom des interfaces. En Gin, celles-ci s'appellent Ginjector, GinModule et AbstractGinModule tandis qu'avec Guice, elles se nomment Injector, Module et AbstractModule.
La seconde est que Gin ne possède qu'un sous-ensemble de fonctionnalités de Guice. Pour vous rendre compte, Gin fournit un tableau comparatif entre les différentes versions de Gin et de Guice : Guice Compatibility.
Enfin, si vous souhaitez réutiliser le code écrit avec Gin, dans Guice, un adapteur est fourni, GinModuleAdapter qui vous permet par exemple de partager du code entre le client et le serveur (j'avoue personnellement ne voir qu'un intérêt limité à cette fonctionnalité mais qui sait... mieux vaut en avoir trop que pas assez).
IV. Conclusion▲
Pour conclure et vous donner mon avis, utilisez Gin. Pour l'instant il n'existe pas d'équivalent pour la partie cliente de GWT, et le coût de mise en place est négligeable comparé au gain que vous pourrez en retirer, à la fois en termes de productivité mais aussi de maintenabilité de code.
Le lien du projet : http://code.google.com/p/google-gin/.
V. Remerciements▲
Cet article a été publié avec l'aimable autorisation de la société ZenikaZenika, le billet original peut être trouvé sur le blog de ZenikaBlog de Zenika.
Nous tenons à remercier ClaudeLELOUP pour sa relecture attentive de cet article puis djibril et Mickael Baron pour la mise au gabarit du billet original.