J'ai lu dans quelques articles sur Spring MVC
et Portlets
que l'injection de champ n'est pas recommandée. Parce que j'essaie d'obtenir un Alors je me suis demandé si j'utilisais injection de champ et je ne peux pas y répondre. Si je comprends bien injection de champ si vous injectez un Bean
dans un attribut avec @Autowired
comme ceci:
CartController.Java:
...
@Autowired
private Cart cart;
...
BookshopConfiguartion.Java:
@Configuration
public class BookShopConfiguration {
@Bean
public Cart cart(){
return new Cart();
}
//more configuration
Mon Cart.Java
est utilisé pour stocker et fournir des informations sur les livres dans le panier.
Pendant mes recherches, j'ai lu sur constructeur constructeur :
MyComponent.Java:
...
public class MyComponent{
private Cart cart;
@Autowired
public MyComponent(Cart cart){
this.cart = cart;
}
...
Quels sont les avantages et les inconvénients de ces deux types d'injections?
EDIT 1: Comme cette question est marquée comme un doublon de cette question je l'ai vérifiée. Parce qu'il n'y a pas d'exemples de code ni dans la question ni dans les réponses, il m'est difficile de savoir si je suis correct avec ma conjecture quel type d'injection j'utilise.
Types d'injection
Il existe trois options pour l'injection de dépendances dans un bean:
Vous utilisez l'option 3. C'est ce qui se produit lorsque vous utilisez @Autowired
directement sur votre champ.
Directives pour l'injection
Une directive générale, recommandée par Spring (voir les sections sur DI basée sur le constructeur ou DI sur la base ) est la suivante:
Inconvénients de l'injection de champ
Les raisons pour lesquelles l'injection sur le terrain est mal vue sont les suivantes:
Conclusion
Selon vos besoins, vous devez principalement utiliser une injection de constructeur ou un mélange d'injection de constructeur et de setter. L'injection sur le terrain présente de nombreux inconvénients et doit être évitée. Le seul avantage de l'injection sur le terrain est qu'il est plus pratique d'écrire, ce qui ne l'emporte pas sur tous les inconvénients.
Autres lectures
J'ai écrit un article de blog sur les raisons pour lesquelles l'injection sur le terrain n'est généralement pas recommandée: injection de dépendance sur le terrain considérée comme nuisible .
C’est l’une des discussions sans fin dans le développement logiciel, mais les grands influenceurs du secteur ont de plus en plus d’opinions sur le sujet et ont commencé à suggérer l’injection de constructeur comme la meilleure option.
Injection de constructeur
Avantages:
Les inconvénients:
Fondamentalement, l'injection de champ est le contraire.
Question de goût. C'est votre décision.
Mais je peux expliquer pourquoi je n’utilise jamais l’injection de constructeur .
Je ne souhaite pas implémenter de constructeur pour tous mes beans @Service
, @Repository
et @Controller
. Je veux dire, il y a environ 40-50 haricots ou plus. A chaque fois que j'ajoute un nouveau champ, je devrais étendre le constructeur. Non, je ne le veux pas et je n'ai pas à le faire.
Que se passe-t-il si votre haricot (service ou contrôleur) nécessite l'injection de beaucoup d'autres haricots? Un constructeur avec 8 paramètres est très moche.
Si j'utilise CDI, le constructeur ne me concerne pas.
EDIT: Vojtech Ruzicka a dit:
la classe a trop de dépendances et viole probablement le principe de responsabilité unique et devrait être refondue
Oui. Théorie et réalité. Voici un exemple: DashboardController
mappé sur un chemin unique *:8080/dashboard
.
Mon DashboardController
collecte beaucoup d'informations d'autres services pour les afficher dans une page de tableau de bord/système. J'ai besoin de ce contrôleur unique. Je ne dois donc sécuriser que ce seul chemin (filtre d'authentification de base ou de rôle d'utilisateur).
Field
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
Qu'est-ce qui ne va pas? Comme vous pouvez le constater, la variante du champ est très jolie. C'est très court, concis, il n'y a pas de code standard. Le code est facile à lire et à naviguer. Votre classe ne peut que se concentrer sur l’important et n’est pas polluée par le blindage DI. Vous venez de mettre l'annotation @Autowired au-dessus des champs et c'est tout. Pas de constructeurs spéciaux ou des setters juste pour le conteneur DI pour fournir vos dépendances. Java est très prolixe, donc chaque opportunité de raccourcir votre code est la bienvenue, non?
Violation du principe de responsabilité unique
Il est très facile d'ajouter de nouvelles dépendances. Peut-être trop facile. Il n’ya aucun problème à ajouter six, dix ou même douze dépendances. Lorsque vous utilisez des constructeurs pour DI, après un certain point, le nombre de paramètres de constructeur devient trop élevé et il est immédiatement évident que quelque chose ne va pas. Avoir trop de dépendances signifie généralement que la classe a trop de responsabilités. Cela peut constituer une violation du principe de responsabilité unique et une séparation des préoccupations. C'est un bon indicateur du fait que la classe doit faire l'objet d'une inspection supplémentaire et éventuellement d'une refactorisation. Il n’existe pas de drapeau rouge lors de l’injection directe dans les champs car cette approche peut évoluer indéfiniment.
masquage de dépendance
L'utilisation du conteneur DI signifie que la classe n'est plus responsable de la gestion de ses propres dépendances. La responsabilité d'obtenir les dépendances est extraite de la classe. Quelqu'un d'autre est maintenant responsable de fournir les dépendances - le conteneur DI ou de les assigner manuellement dans les tests. Lorsque la classe n'est plus responsable de l'obtention de ses dépendances, elle doit clairement les communiquer à l'aide de méthodes ou de constructeurs d'interface publique. De cette façon, il est clair ce que la classe requiert et aussi si elle est optionnelle (setters) ou obligatoire (constructeurs).
couplage de conteneur DI
L'une des idées fondamentales des frameworks DI est que la classe gérée ne doit pas dépendre du conteneur DI utilisé. En d'autres termes, il doit s'agir d'un simple POJO, qui peut être instancié indépendamment, à condition de lui transmettre toutes les dépendances requises. De cette façon, vous pouvez l'instancier dans un test unitaire sans démarrer le conteneur DI et le tester séparément (avec un conteneur qui serait davantage un test d'intégration). S'il n'y a pas de couplage de conteneur, vous pouvez utiliser la classe en tant que gérée ou non gérée ou même basculer vers un nouveau cadre DI.
Cependant, lorsque vous injectez directement dans des champs, vous ne fournissez aucun moyen direct d'instancier la classe avec toutes ses dépendances requises. Cela signifie:
Il existe un moyen (en appelant le constructeur par défaut) de créer un objet à l'aide de new dans un état qui manque de certains de ses collaborateurs obligatoires et dont l'utilisation entraînerait le NullPointerException
. Une telle classe ne peut pas être réutilisée en dehors des conteneurs DI (tests, autres modules) car il n’est pas possible, sauf réflexion, de lui fournir les dépendances requises. Immutabilité Contrairement au constructeur, l'injection de champ ne peut pas être utilisée pour affecter des dépendances aux champs finaux rendant ainsi vos objets mutables.