web-dev-qa-db-fra.com

Qu'est-ce que l'injection de champ et comment l'éviter?

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.

74
T. Jung

Types d'injection

Il existe trois options pour l'injection de dépendances dans un bean:

  1. À travers un constructeur
  2. Par des setters ou d'autres méthodes
  3. Par réflexion, directement dans les champs

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:

  • Pour les dépendances obligatoires ou lorsque l’immuabilité est recherchée, utilisez l’injection par constructeur.
  • Pour les dépendances optionnelles ou modifiables, utilisez l'injection de setter
  • Éviter l'injection sur le terrain dans la plupart des cas

Inconvénients de l'injection de champ

Les raisons pour lesquelles l'injection sur le terrain est mal vue sont les suivantes:

  • Vous ne pouvez pas créer d'objets immuables, comme vous pouvez le faire avec l'injection de constructeur
  • Vos classes ont un couplage étroit avec votre conteneur DI et ne peuvent pas être utilisées en dehors de celui-ci.
  • Vos classes ne peuvent pas être instanciées (par exemple dans des tests unitaires) sans réflexion. Vous avez besoin du conteneur DI pour les instancier, ce qui rend vos tests plus similaires aux tests d'intégration
  • Vos dépendances réelles sont cachées de l'extérieur et ne sont pas reflétées dans votre interface (constructeurs ou méthodes)
  • C'est vraiment facile d'avoir comme dix dépendances. Si vous utilisiez l'injection de constructeur, vous auriez un constructeur avec dix arguments, ce qui indiquerait que quelque chose est louche. Mais vous pouvez ajouter des champs injectés en utilisant l’injection de champ indéfiniment. Avoir trop de dépendances est un signal d'alarme que la classe fait généralement plus d'une chose et qu'elle peut enfreindre le principe de responsabilité unique.

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 .

134
Vojtech Ruzicka

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:

  • Meilleure testabilité . Vous n'avez pas besoin de bibliothèque moqueuse ni de contexte Spring dans les tests unitaires. Vous pouvez créer un objet que vous souhaitez tester avec le mot clé new . De tels tests sont toujours plus rapides car ils ne reposent pas sur le mécanisme de réflexion. ( Cette question a été posée 30 minutes plus tard. Si l'auteur avait utilisé l'injection de constructeur, elle ne serait pas apparue).
  • Immutabilité . Une fois les dépendances définies, elles ne peuvent plus être modifiées.
  • Code plus sûr . Après l'exécution d'un constructeur, votre objet est prêt à être utilisé car vous pouvez valider tout ce qui a été passé en tant que paramètre. L'objet peut être prêt ou non, il n'y a pas d'état entre les deux. Avec l'injection sur le terrain, vous introduisez une étape intermédiaire lorsque l'objet est fragile.
  • Expression plus propre des dépendances obligatoires . L'injection sur le terrain est ambiguë à cet égard.
  • Fait réfléchir les concepteurs à la conception . dit a écrit sur un constructeur à 8 paramètres, ce qui est en fait le signe d’un mauvais design et l’anti-motif de Die . Peu importe qu'une classe ait 8 dépendances dans son constructeur ou dans des champs, c'est toujours faux. Les gens sont plus réticents à ajouter plus de dépendances à un constructeur que via des champs. Cela fonctionne comme un signal à votre cerveau que vous devriez vous arrêter un moment et réfléchir à la structure de votre code.

Les inconvénients:

  • Plus de code (mais les IDE modernes atténuent la douleur).

Fondamentalement, l'injection de champ est le contraire.

33
Daniel Olszewski

Question de goût. C'est votre décision.

Mais je peux expliquer pourquoi je n’utilise jamais l’injection de constructeur .

  1. 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.

  2. 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.

  3. 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).

22
dieter
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.

3
pramodgautam