Il y a 14 ans -
Temps de lecture 3 minutes
Google Guice : Injection avancée
Dans le cadre d’un article d’introduction à Guice, nous avions vu une injection de dépendance simple, répondant à un besoin relativement basique.
Dans ce second article, nous allons découvrir des outils d’injection plus évolués, qui devraient nous permettre de réaliser par la suite notre premier exemple ‘réel’ d’implémentation Guice.
Injection ‘nommée’
Reprenons l’exemple simple utilisé dans l’article précédent pour en augmenter la difficulté.
Nous partons du postulat que notre DAO ne possède plus une seule, mais deux implémentations distinctes : MyBasicDaoImpl
, précédemment utilisée, et MyBasicDaoMock
, une nouvelle implémentation utilisant une base volatile.
Nous allons utiliser ces deux implémentations successivement dans notre service, et donc injecter deux implémentations différentes de la même interface dans notre service :
@Override public void displaySample() { try { recorder.append("Calling DAOn"); String daoResult = basicDao.select(); recorder.append("Result from DAO : " + daoResult + "n"); recorder.append("Calling memory DAOn"); daoResult = memoryDao.select(); recorder.append("Result from memory DAO : " + daoResult + "n"); } catch (IOException e) { e.printStackTrace(); } }
Premier point à noter, pour notre DAO existant rien ne change, Guice utilisera la première implémentation déclarée pour résoudre la dépendance.
Pour notre seconde dépendance, nous utiliserons l’injection nommée.
Première solution, créer une annotation, @Memory
, en utilisant la BindingAnnotation
de Guice :
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) @BindingAnnotation public @interface Memory {}
Il suffit alors d’annoter le constructeur du service :
@Inject public MyServiceImpl(MyBasicDao basicDao, @Memory MyBasicDao memoryDao) { super(); this.basicDao = basicDao; this.memoryDao = memoryDao; }
et d’annoter la déclaration de dépendance dans la classe Module
binder.bind(MyBasicDao.class).annotatedWith(Memory.class) .to(MyBasicDaoMemoryImpl.class);
Cette solution est relativement élégante, mais comporte le défaut de multiplier les classes d’annotation.
Le même résultat peut être atteint en utilisant une classe Guice permettant de nommer ses classes, mais sans créer d’annotation spécifique, à l’aide de l’annotation @Named
et de la méthode Names.named(String)
Le code ci-dessus est équivalent à :
// Dans MyModule binder.bind(MyBasicDao.class).annotatedWith(Names.named("Memory")) .to(MyBasicDaoMemoryImpl.class); // Dans MyServiceImpl @Inject public MyServiceImpl(MyBasicDao basicDao, @Named("Memory") MyBasicDao memoryDao) { ... }
Utiliser une dépendance conditionnelle à l’aide de l’annotation @Provides
Continuons à affiner notre injection de dépendance, en la conditionnant à des conditions runtime.
Imaginons que nous voulions injecter une implémentation de notre DAO en dépendant d’un paramètre runtime (nous nous baserons simplement sur un nombre aléatoire).
Pour cela, nous allons utiliser une méthode @Provides
(à ne pas confondre avec les Providers
de la version 1 de Guice).
Cette méthode doit être placée dans un module (où elle sera auto-découverte et retournera une instance de la classe injectée).
// Dans MyModule /** * Provides method */ @Provides @Inject private @Named("Random") MyBasicDao provideRandomDao(MyBasicDao basicDao, @Named("Memory") MyBasicDao memoryDao) { if (RandomUtils.randomBoolean()) { return basicDao; } return memoryDao; }
On peut noter que cette méthode peut elle-même être injectée (avec les instances de Dao précédemment utilisées).
Conclusion
Nous avons maintenant une certaine souplesse dans l’injection, qui va nous permettre d’aborder dans un prochain billet un exemple plus concret.
À noter que durant la rédaction de ce billet, la version 2.0 de Google Guice a été officiellement releasée, et que les prochains billets se baseront sur cette version stable.
Commentaire