J'ai posé une question générale sur Spring: les beans Spring auto-castés et plusieurs personnes ont répondu qu'il ApplicationContext.getBean()
fallait éviter autant que possible d' appeler Spring's . Pourquoi donc?
Sinon, comment puis-je accéder aux beans que j'ai configurés pour créer Spring?
J'utilise Spring dans une application non Web et j'avais prévu d'accéder à un ApplicationContext
objet partagé comme décrit par LiorH .
Amendement
J'accepte la réponse ci-dessous, mais voici une autre prise de Martin Fowler qui discute des mérites de l'injection de dépendance par rapport à l'utilisation d'un localisateur de service (qui est essentiellement la même chose que d'appeler un wrappé ApplicationContext.getBean()
).
En partie, Fowler déclare: " Avec le localisateur de service, la classe d'application le demande [le service] explicitement par un message au localisateur. Avec l'injection, il n'y a pas de demande explicite, le service apparaît dans la classe d'application - d'où l'inversion du contrôle. L'inversion de contrôle est une caractéristique courante des frameworks, mais c'est quelque chose qui a un prix. Elle a tendance à être difficile à comprendre et à poser des problèmes lorsque vous essayez de déboguer. Donc, dans l'ensemble, je préfère l'éviter [Inversion de contrôle ] sauf si j'en ai besoin. Cela ne veut pas dire que c'est une mauvaise chose, mais je pense que cela doit se justifier par rapport à l'alternative la plus simple. "
new MyOtherClass()
objet? Je connais @Autowired, mais je ne l'ai utilisé que sur des champs et il se décomposenew MyOtherClass()
..applicationContext.getBean
n'est pas une injection de dépendance: il accède directement au framework, l'utilise comme localisateur de service .Provider<Foo>
au lieu d'unFoo
et en appelantprovider.get()
chaque fois que vous avez besoin d'un nouvelle instance. Aucune référence au conteneur lui-même, et vous pouvez facilement en créer unProvider
pour le test.Les raisons de préférer Service Locator à Inversion of Control (IoC) sont:
Service Locator est beaucoup, beaucoup plus facile pour les autres personnes à suivre votre code. L'IoC est «magique», mais les programmeurs de maintenance doivent comprendre vos configurations de Spring alambiquées et toute la myriade d'emplacements pour comprendre comment vous avez câblé vos objets.
L'IoC est terrible pour le débogage des problèmes de configuration. Dans certaines classes d'applications, l'application ne démarre pas lorsqu'elle est mal configurée et vous risquez de ne pas pouvoir passer en revue ce qui se passe avec un débogueur.
L'IoC est principalement basé sur XML (les annotations améliorent les choses mais il y a encore beaucoup de XML). Cela signifie que les développeurs ne peuvent pas travailler sur votre programme à moins qu'ils ne connaissent toutes les balises magiques définies par Spring. Il ne suffit plus de connaître Java. Cela gêne les programmeurs moins expérimentés (c'est-à-dire qu'il est en fait de mauvaise conception d'utiliser une solution plus compliquée lorsqu'une solution plus simple, comme Service Locator, répondra aux mêmes exigences). De plus, la prise en charge du diagnostic des problèmes XML est beaucoup plus faible que la prise en charge des problèmes Java.
L'injection de dépendance est plus adaptée aux programmes plus importants. La plupart du temps, la complexité supplémentaire n'en vaut pas la peine.
Souvent, Spring est utilisé au cas où vous "voudriez changer la mise en œuvre plus tard". Il existe d'autres moyens d'y parvenir sans la complexité de Spring IoC.
Pour les applications Web (Java EE WARs), le contexte Spring est effectivement lié au moment de la compilation (sauf si vous voulez que les opérateurs explorent le contexte dans la guerre éclatée). Vous pouvez faire en sorte que Spring utilise les fichiers de propriétés, mais avec les servlets, les fichiers de propriétés devront se trouver à un emplacement prédéterminé, ce qui signifie que vous ne pouvez pas déployer plusieurs servlets en même temps sur la même boîte. Vous pouvez utiliser Spring avec JNDI pour modifier les propriétés au démarrage de la servlet, mais si vous utilisez JNDI pour des paramètres modifiables par l'administrateur, le besoin de Spring lui-même diminue (puisque JNDI est en fait un localisateur de service).
Avec Spring, vous pouvez perdre le contrôle du programme si Spring se répartit dans vos méthodes. C'est pratique et fonctionne pour de nombreux types d'applications, mais pas pour toutes. Vous devrez peut-être contrôler le flux du programme lorsque vous devez créer des tâches (threads, etc.) pendant l'initialisation ou avoir besoin de ressources modifiables que Spring ignorait quand le contenu était lié à votre WAR.
Le printemps est très bon pour la gestion des transactions et présente certains avantages. C'est juste que l'IoC peut être une ingénierie excessive dans de nombreuses situations et introduire une complexité injustifiée pour les mainteneurs. N'utilisez pas automatiquement l'IoC sans penser à des moyens de ne pas l'utiliser en premier.
la source
Il est vrai que l'inclusion de la classe dans application-context.xml évite d'avoir à utiliser getBean. Cependant, même cela n'est en fait pas nécessaire. Si vous écrivez une application autonome et que vous NE VOULEZ PAS inclure votre classe de pilote dans application-context.xml, vous pouvez utiliser le code suivant pour que Spring transfère automatiquement les dépendances du pilote:
J'ai dû le faire plusieurs fois lorsque j'ai une sorte de classe autonome qui doit utiliser un aspect de mon application (par exemple pour les tests) mais je ne veux pas l'inclure dans le contexte de l'application car ce n'est pas fait partie de l'application. Notez également que cela évite d'avoir à rechercher le bean en utilisant un nom de chaîne, ce que j'ai toujours trouvé laid.
la source
@Autowired
annotation.L'un des avantages les plus intéressants de l'utilisation de quelque chose comme Spring est que vous n'avez pas à câbler vos objets ensemble. La tête de Zeus s'ouvre et vos classes apparaissent, entièrement formées avec toutes leurs dépendances créées et câblées, selon les besoins. C'est magique et fantastique.
Plus vous en dites
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, moins vous obtenez de magie. Moins de code est presque toujours meilleur. Si votre classe avait vraiment besoin d'un bean ClassINeed, pourquoi ne l'avez-vous pas simplement connecté?Cela dit, quelque chose doit évidemment créer le premier objet. Il n'y a rien de mal à ce que votre méthode principale acquière un bean ou deux via getBean (), mais vous devriez l'éviter car chaque fois que vous l'utilisez, vous n'utilisez pas vraiment toute la magie de Spring.
la source
La motivation est d'écrire du code qui ne dépend pas explicitement de Spring. De cette façon, si vous choisissez de changer de conteneur, vous n'avez pas à réécrire de code.
Considérez le conteneur comme quelque chose d'invisible pour votre code, répondant par magie à ses besoins, sans qu'on le lui demande.
L'injection de dépendances est un contrepoint au modèle "localisateur de services". Si vous allez rechercher des dépendances par nom, vous pourriez aussi bien vous débarrasser du conteneur DI et utiliser quelque chose comme JNDI.
la source
Utiliser
@Autowired
ouApplicationContext.getBean()
c'est vraiment la même chose. Dans les deux cas, vous obtenez le bean configuré dans votre contexte et dans les deux cas, votre code dépend du printemps. La seule chose à éviter est d'instancier votre ApplicationContext. Faites cela une seule fois! En d'autres termes, une ligne commene doit être utilisé qu'une seule fois dans votre application.
la source
L'idée est que vous comptez sur l'injection de dépendances ( inversion de contrôle , ou IoC). Autrement dit, vos composants sont configurés avec les composants dont ils ont besoin. Ces dépendances sont injectées (via le constructeur ou les setters) - vous n'obtenez pas alors vous-même.
ApplicationContext.getBean()
vous oblige à nommer un bean explicitement dans votre composant. Au lieu de cela, en utilisant IoC, votre configuration peut déterminer quel composant sera utilisé.Cela vous permet de recâbler facilement votre application avec différentes implémentations de composants, ou de configurer des objets pour les tests de manière simple en fournissant des variantes simulées (par exemple, un DAO simulé afin de ne pas toucher une base de données pendant les tests)
la source
D'autres ont souligné le problème général (et sont des réponses valables), mais je vais juste faire un commentaire supplémentaire: ce n'est pas que vous ne devriez JAMAIS le faire, mais plutôt que vous le fassiez le moins possible.
Cela signifie généralement que cela se fait exactement une fois: lors du démarrage. Et puis c'est juste pour accéder au bean "root", à travers lequel d'autres dépendances peuvent être résolues. Il peut s'agir de code réutilisable, comme le servlet de base (si vous développez des applications Web).
la source
L'une des prémisses de Spring est d'éviter le couplage . Définissez et utilisez des interfaces, DI, AOP et évitez d'utiliser ApplicationContext.getBean () :-)
la source
L'une des raisons est la testabilité. Disons que vous avez cette classe:
Comment pouvez-vous tester ce bean? Par exemple, comme ceci:
Facile, non?
Bien que vous dépendiez toujours de Spring (en raison des annotations), vous pouvez supprimer votre dépendance à Spring sans modifier aucun code (uniquement les définitions d'annotations) et le développeur de test n'a besoin de rien savoir sur le fonctionnement de Spring (peut-être qu'il devrait de toute façon, mais il permet de revoir et de tester le code séparément de ce que fait le ressort).
Il est toujours possible de faire de même lors de l'utilisation d'ApplicationContext. Cependant, vous devez vous moquer de
ApplicationContext
ce qui est une énorme interface. Vous avez besoin d'une implémentation factice ou vous pouvez utiliser un cadre de simulation tel que Mockito:C'est tout à fait une possibilité, mais je pense que la plupart des gens conviendraient que la première option est plus élégante et rend le test plus simple.
La seule option qui pose vraiment problème est celle-ci:
Tester cela nécessite des efforts énormes ou votre bean va tenter de se connecter à stackoverflow à chaque test. Et dès que vous avez une panne de réseau (ou que les administrateurs de stackoverflow vous bloquent en raison d'un taux d'accès excessif), vous aurez des tests qui échouent au hasard.
Donc, en guise de conclusion, je ne dirais pas que l'utilisation
ApplicationContext
directe est automatiquement erronée et doit être évitée à tout prix. Cependant, s'il existe de meilleures options (et il y en a dans la plupart des cas), utilisez les meilleures options.la source
Je n'ai trouvé que deux situations où getBean () était requis:
D'autres ont mentionné l'utilisation de getBean () dans main () pour récupérer le bean "principal" pour un programme autonome.
Une autre utilisation que j'ai faite de getBean () est dans des situations où une configuration utilisateur interactive détermine la composition du bean pour une situation particulière. Ainsi, par exemple, une partie du système de démarrage passe en boucle dans une table de base de données en utilisant getBean () avec une définition de bean scope = 'prototype', puis en définissant des propriétés supplémentaires. Vraisemblablement, il existe une interface utilisateur qui ajuste la table de base de données qui serait plus conviviale que de tenter de (ré) écrire le XML du contexte de l'application.
la source
Il y a un autre moment où l'utilisation de getBean est logique. Si vous reconfigurez un système qui existe déjà, où les dépendances ne sont pas explicitement appelées dans les fichiers de contexte de Spring. Vous pouvez démarrer le processus en envoyant des appels à getBean, afin de ne pas avoir à tout câbler en même temps. De cette façon, vous pouvez lentement construire votre configuration de ressort en mettant chaque pièce en place au fil du temps et en alignant correctement les mèches. Les appels à getBean seront éventuellement remplacés, mais si vous comprenez la structure du code, ou si vous n'en avez pas, vous pouvez démarrer le processus de câblage de plus en plus de beans et d'utiliser de moins en moins d'appels pour getBean.
la source
cependant, il existe encore des cas où vous avez besoin du modèle de localisateur de service. par exemple, j'ai un bean de contrôleur, ce contrôleur peut avoir des beans de service par défaut, qui peuvent être des dépendances injectées par la configuration. Bien qu'il puisse également y avoir de nombreux services supplémentaires ou nouveaux que ce contrôleur peut appeler maintenant ou plus tard, qui ont ensuite besoin du localisateur de services pour récupérer les beans de service.
la source
Vous devez utiliser: ConfigurableApplicationContext au lieu de pour ApplicationContext
la source