Guicy : un cocktail de Groovy et de Google Guice

Je suis récemment tombé sur le nouveau framework d'IoC/DI de Bob Lee: Google Guice. J'ai plutôt tendance à utiliser Spring pour ce genre de tâche, entre autre parce que Spring va beaucoup plus loin que simplement l'IoC/DI, mais je me suis dit que j'allais laissé sa chance à Guice, et que ça allait me permettre de jouer avec le support des annotations dans Groovy. Donc j'ai téléchargé Guice, et j'ai leu la documentation. Par ailleurs, j'ai pris un "snapshot" de Groovy 1.1 qui supporte les annotations Java 5. Puis avec guice-1.0.jar et aopalliance.jar dans mon classpath et la dernière version de Groovy installée, j'étais prêt à expérimenter !

Alors, par où commence-t'on ? Et bien, tout d'abord, il nous faut un contrat de service dont notre code client dépendra et que l'on pourra injecter. Rien de bien sorcier, j'ai honteusement copié l'exemple de la documentation.

interface Service {
void go()
}

Une fois ce service développé, il nous faut une implémentation concrète :

class ServiceImpl implements Service {
void go() {
println "Okay, I'm going somewhere"
}
}

Jusque là, tout va bien. Maintenant, il nous faut le code client qui a besoin d'une petite injection en intra-veineuse. C'est là que nous allons commencer à voir les juteuses annotations Guice poindre le bout de leur nez.

class ClientWithCtor {

private final Service service  

@Inject  
Client(Service service) {  
    this.service = service  
}  

void go() {  
    service.go()  
}  

}

Ici, je crée une classe client où j'utilise l'injection par constructeur. La seule chose que nous ayons à faire, c'est de mettre une annotation @Inject sur le constructeur. Mais pour le moment, nous n'avons effectué aucun tissage, et c'est ce que nous allons faire maintenant. Guice propose un concepte de Module qui contient la configuration programmatique de nos beans.

class MyModule implements Module {
void configure(Binder binder) {
binder.bind(Service)
.to(ServiceImpl)
.in(Scopes.SINGLETON)
}
}

On relie le contrat de l'interface de Service avec l'implémentation concrète ServiceImpl. Je spécifie également la portée de l'injection : je veux que cette implémentation soit unique dans toute mon application (un singleton, pour les intimes). A noter : au lieu de spécifier la portée dans le module, il est également possible d'utiliser l'annotation @Singleton sur la classse ServiceImpl.

Maintenant que tout est en place, nous allons créée un injecteur Guice et récupérer un client proprement configuré avec le code suivant :

def injector = Guice.createInjector(new MyModule())

def clientWithCtor = injector.getInstance(ClientWithCtor)
clientWithCtor.go()

Au lieu d'utiliser l'approche d'injection par constructeur, on peut préférer l'injection par setter. Et comme Groovy génère automatiquement les getters et setters associés aux propriétés de classes, le code devient même plus court et plus lisible :

class ClientWithSetter {

@Inject Service service  

void go() {  
    service.go()  
}  

}

Pour effectuer l'injection et récupérer la classe client, on opère de la même façon que tout à l'heure :

def clientWithSetter = injector.getInstance(ClientWithSetter)
clientWithSetter.go()

Je ne pense pas que j'utiliserai Guice sur un projet client dans l'immédiant, mais pour de petits projets où j'ai envie de m'abstraire de l'enfer de la configuration XML, Guice peut être un bon choix. Malgré tout, je serais peut-être tenté d'utilisé le Spring bean builder de Grails pour faire de la configuration programmatique, car c'est une bonne tactique pour faire oublier le goût amer des kilomètres de tag XML auxquels Spring nous a habitué. D'autre part, en conclusion, le support des annotations dans Groovy fonctionne plutôt bien, comme démontré récemment par Romain Guy lorsqu'il integre Groovy et JPA. Je suis sûr qu'avec le support des annotations (un cas unique dans les langages dynamiques pour la JVM) et avec tous les frameworks les utilisant, Groovy va devenir le standard de fait comme solution de scripting pour les applications d'entreprise.