Il y a 14 ans -
Temps de lecture 4 minutes
Google Guice 2 – Les bases de l’injection de dépendances
Guice (prononcez Juice) est le framework d’injection de dépendances de Google. La configuration des dépendances se fait par code, à l’aide d’annotations, et nécessite donc l’utilisation de Java 5.
Google travaille actuellement sur la V2 de son framework, qui tarde à sortir. Cependant, une mise à jour régulière du Wiki du projet et la publication de snapshots permettent d’ores et déjà de se faire une bonne idée de cette alternative à l’injection de dépendance ‘à la Spring’.
Nous entamons aujourd’hui une série d’articles qui a pour but de vous faire toucher du doigt la grande liberté qu’offre l’injection de dépendance par code. Nous irons progressivement des concepts de base de Guice 2.0, vers une utilisation avancée du framework.
Déclarer une dépendance avec l’annotation @Inject.
Prenons le postulat de départ suivant : la classe MyServiceImpl (qui implémente l’interface MyService) doit recevoir l’injection de la classe MyBasicDaoImpl (qui implémente l’interface MyBasicDao).
Dans le code source de la classe à injecter, nous allons donc indiquer que Guice doit s’occuper de cette injection, en utilisant l’annotation com.google.inject.Inject. Dans ce premier exemple, nous positionnerons cette annotation au niveau du constructeur.
En outre, nous déclarons un Appendable qui nous permettra de réaliser des traces. Cet appendable sera injecté directement dans la variable.
public class MyServiceImpl implements MyService { @Inject private Appendable recorder; private final MyBasicDao basicDao; @Inject public MyServiceImpl(MyBasicDao basicDao) { super(); this.basicDao = basicDao; } }
Vous remarquerez que grâce à l’injection par constructeur, nous sommes en mesure de déclarer la variable basicDao finale.
L’injection peut être réalisée au niveau du constructeur, d’une méthode voire même de la variable elle-même. Quelques avantages / inconvénients de ces différentes méthodes :
- Constructeur
- + Les variables peuvent être déclarées finales
- – Ne peut être utilisé si l’objet est instancié hors de Guice
- Méthode
- + Permet d’injecter un objet instancié hors de Guice
- + Les classes filles n’ont pas besoin de connaître les dépendances des classes mères.
- Variable
- + Syntaxe la plus compacte
- – Réduit la testabilité et l’encapsulation
La méthode recommandée par Google est l’injection par constructeur.
Lier une interface à son implémentation réelle, dans un Module
Il nous faut maintenant lier l’interface MyBasicDao à l’implémentation que nous souhaitons utiliser dans un premier temps, à savoir MyBasicDaoImpl.
Ceci se fait dans un module de configuration étendant com.google.inject.Module à l’aide d’un DSL relativement explicite.
public class MyModule implements com.google.inject.Module { @Override public void configure(com.google.inject.Binder binder) { // Bind to class binder.bind(MyBasicDao.class).to(MyBasicDaoImpl.class); binder.bind(MyService.class).to(MyServiceImpl.class); // Bind to instance binder.bind(Appendable.class).toInstance(System.out); } }
Le Module permet de lier des interfaces à des classes que Guice se chargera d’instancier (_bind … to_) mais aussi à des classes déjà instanciées (_bind … toInstance_) comme c’est le cas pour notre Appendable, qui sera lié à la console System.out.
Récupérer une instance auprès de l’Injector
Dernière étape, il nous faut obtenir une instance injectée de notre service.
C’est auprès de la classe com.google.inject.Injector que nous la récupérerons. Cet injecteur est créé de manière statique, à partir d’un ou plusieurs Module.
Dans cet exemple, nous réaliserons cette opération à partir d’un main.
public class MyMain { public static void main(String[] args) throws IOException { Injector injector = Guice.createInjector(new MyModule()); MyService myService = injector.getInstance(MyService.class); myService.displaySample(); } }
Et Spring dans tout ca ?
La question qui se pose dès que l’on aborde l’injection de dépendances est bien sûr ‘Oui, mais par rapport à Spring ?’.
Essayons d’éviter la réponse de normand, même si Spring et Guice ne jouent pas dans la même cour : Guice se concentre exclusivement sur l’injection de dépendances, alors que Spring vise à offrir une stack complète.
L’équipe de Guice répond d’ailleurs elle-même à la question sur son wiki.
Cependant, pour donner un point de vue plus pragmatique :
- le footprint du Jar du Guice est bien inférieur à celui (ceux) de Spring Core.
- le chaînage des Beans est plus rapide avec Guice, même si cet argument pèse peu, la majorité des dépendances étant chargées à l’initialisation de l’application.
- Guice est plus intrusif (apparition d’annotations dans le code source des services métier)
- la programmation orientée aspect de Guice est au stade embryonnaire.
Dans la suite de cette série d’articles, nous essayerons de montrer que la différence d’approche entre Guice et Spring offre plus de libertés aux développeurs (mais conduit peut-être aussi à plus de chaos).
La suite ?
C’est déjà la fin de ce premier tutoriel, et vous savez maintenant injecter simplement une classe. Cependant, dans le projet tel qu’écrit aujourd’hui, la classe MyBasicDaoImpl sera injectée dans toutes les classes nécessitant un MyBasicDao. Dans un prochain article, nous verrons comment lever cette limite.
Téléchargez le projet Eclipse sur le Google Code de Xebia
Ressources
Site officiel
Un comparatif de performances Spring / Guice, basé sur les versions précédentes
Commentaire
4 réponses pour " Google Guice 2 – Les bases de l’injection de dépendances "
Published by pariviere , Il y a 14 ans
Exactement dans le même genre, on a bien entendu tapestry-ioc (http://tapestry.apache.org/tapestry5/tapestry-ioc/) qui d’ailleurs s’en inspire clairement.
Published by Séven Le Mesle , Il y a 14 ans
Oui, nous prévoyons d’ailleurs de faire un article de présentation de la méthode tapestry-ioc dans les prochains jours.
Pour rappel il s’agit d’un fork de hivemind.
Published by Fanf , Il y a 14 ans
A noter que l’un des gros apports de T5-IoC par rapport à Guice est la possibilité de configurer ses services de manière décentralisée, c’est à dire que différents modules, dans différents jars, peuvent « participer » à la configuration d’un service défini dans un module. Pour plus d’information sur cette petite merveille de modularité (sans compter que couplé au chargement automatique des modules T5-IoC situés dans le classpath, créer un système de plugins pour votre application devient gratuit…), voir : http://tapestry.apache.org/tapestry5.1/tapestry-ioc/configuration.html
Pour ce qui est de Hivemind, T5-IoC n’en n’est pas un fork, dans le sens où il n’y a aucune base de code commune entre les deux projets. Mais comme dans les deux cas, le créateur est Howard Lewis Ship, il y a forcément de grosses ressemblances – un peut comme Tapestry 5 n’est pas un fork de Tapestry 4, mais ils ont clairement des similarités.
Published by Anthony MULLER , Il y a 13 ans
Hello,
Il est assez simple de créer un système similaire avec Guice (scan du classpath pour collecter les modules), mais effectivement, ce n’est pas ‘built-in »…
Le gros inconvénient de cette technique est que ça ne fonctionne pas dans un environnement OSGi où les classloaders sont isolés les uns des autres…
Anthony