Qu'est-ce que L'Injection sur le terrain et comment l'éviter?

j'ai lu dans certains billets sur Spring MVC et Portlets que l'injection sur le terrain n'est pas recommandée. Parce que j'essaie d'obtenir un Alors je me suis demandé si j'utilisais field injection et je ne peux pas répondre. D'après ce que j'ai compris, field injection est 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.

au cours de mes recherches, j'ai lu à propos de injection du 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 de des injections?


EDIT 1: comme cette question est marquée comme une copie de cette question Je l'ai vérifiée. Parce qu'il n'y a pas d'exemple de code ni dans la question ni dans les réponses, il n'est pas clair pour moi si j'ai raison de deviner le type d'injection que j'utilise.

42
demandé sur T. Jung 2016-10-06 11:12:05

4 réponses

types D'Injection

il y a trois options pour la façon dont les dépendances peuvent être injectées dans un haricot:

  1. par l'intermédiaire d'un constructeur
  2. par setters ou autres méthodes
  3. par réflexion, directement dans les champs

vous utilisez l'option 3. C'est ce qui arrive lorsque vous utilisez @Autowired directement sur votre domaine.


lignes directrices D'Injection

Une ligne directrice générale, ce qui est recommandé par le Printemps (voir les sections Constructeur à base de DI ou Setter à base de DI ) est le suivant:

  • obligatoire dépendances ou lorsque vous recherchez l'immutabilité, l'utilisation du constructeur d'injection
  • pour facultatif ou des dépendances variables, utiliser setter injection
  • éviter l'injection de terrain dans la plupart des cas

inconvénients de l'injection de terrain

Les raisons pour lesquelles le champ d'injection est mal vu sont comme suit:

  • vous ne pouvez pas créer des objets immuables, comme vous pouvez avec l'injection de constructeur
  • vos classes sont en couple serré avec votre conteneur DI et ne peut pas être utilisé en dehors de celui-ci
  • vos classes ne peuvent pas être instanciées (par exemple dans les tests unitaires) sans réflexion. Vous avez besoin du conteneur DI pour les instancier, ce qui rend vos tests plus comme des tests d'intégration
  • vos dépendances réelles sont cachées de l'extérieur et ne se reflètent pas dans votre interface (qu'il s'agisse de constructeurs ou de méthodes)
  • Il est vraiment facile d'avoir dix dépendances. Si vous 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 violer le principe de Responsabilité Unique.

Conclusion

en fonction de votre besoins, vous devriez principalement utiliser l'injection de constructeur ou un certain mélange de constructeur et setter injection. Champ d'injection comporte 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 la raison pour laquelle l'injection de champ n'est généralement pas recommandée: champ Injection De Dépendance Considérée Comme Nuisible .

91
répondu Vojtech Ruzicka 2018-07-31 22:05:41

C'est l'une des discussions sans fin dans le développement de logiciels, mais les principaux influenceurs dans l'industrie sont de plus en plus opiniâtre sur le sujet et ont commencé à suggérer l'injection de constructeur comme la meilleure option.

injection du constructeur

Pour:

  • meilleure testabilité . Vous n'avez pas besoin d'une bibliothèque moqueuse ou d'un contexte de ressort dans les tests unitaires. Vous pouvez créer un objet que vous voulez tester avec le mot-clé nouveau . Ces tests sont toujours plus rapides, car ils s'appuient pas sur le mécanisme de réflexion. ( cette question a été posée 30 minutes plus tard. Si l'auteur avait utilisé constructeur injection il n'aurait pas paru).
  • immutabilité . Une fois les dépendances définies, elles ne peuvent plus être modifiées.
  • Safer code . 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 paramètre. L'objet peut être prêt ou pas, il n'y a pas d'état entre les deux. Avec field injection vous introduisez une étape intermédiaire lorsque l'objet est fragile.
  • "151930920 plus" propre expression obligatoire des dépendances . L'injection sur le terrain est ambiguë à cet égard.
  • fait penser aux développeurs à propos de la conception . dit a écrit sur un constructeur avec 8 paramètres, qui est en fait le signe d'une mauvaise conception et L'anti-motif de L'objet de Dieu . Peu importe qu'une classe ait 8 dépendances dans son constructeur ou dans des champs, c'est toujours mal. Les gens sont plus réticents à ajouter plus de dépendances à un constructeur que via les champs. Cela fonctionne comme un signal à votre cerveau que vous devriez arrêter pendant un certain temps et penser à votre structure de code.

Inconvénients:

  • plus de code (mais les IDEs modernes soulagent la douleur).

fondamentalement, l'injection de champ est le contraire.

22
répondu Daniel Olszewski 2016-10-06 10:23:49

question de goût. Il est de votre décision.

mais je peux expliquer, pourquoi je n'utilise jamais injection constructeur .

  1. Je ne veux pas mettre en œuvre un constructeur pour tous mes haricots @Service , @Repository et @Controller . Je veux dire, il y a environ 40-50 haricots ou plus. Chaque fois que j'ajoute un nouveau champ, je dois étendre le constructeur. Aucun. Je n'en veux pas et je n'ai pas.

  2. que se passe-t-il si votre haricot (de Service ou de contrôle) nécessite beaucoup d'autres haricots à injecter? Un constructeur avec 8 paramètres est très laid.

  3. si J'utilise CDI, le constructeur ne me concerne pas.


modifier : Vojtech Ruzicka a dit:

La classe

a trop de dépendances et viole probablement célibataire principe de responsabilité et doit être remanié

Oui. La théorie et la réalité. Voici un exemple: DashboardController mappé sur un seul chemin *:8080/dashboard .

mon DashboardController recueille beaucoup d'informations d'autres services pour les afficher dans une page de tableau de bord / Vue d'ensemble du système. J'ai besoin de ce seul contrôleur. Je dois donc sécuriser seulement ce chemin (auth de base ou filtre de rôle de l'utilisateur).

15
répondu dit 2017-05-25 10:25:03
Field
@Autowired
private DependencyA dependencyA;

@Autowired
private DependencyB dependencyB;

@Autowired
private DependencyC dependencyC;

Qu'est-ce qui ne va pas? Comme vous pouvez le voir, la variante de terrain a l'air très agréable. Il est très court, concis, il n'y a pas de code réutilisable. Le code est facile à lire et à naviguer. Votre classe peut simplement se concentrer sur l'important et n'est pas pollué par DI boilerplate. Vous mettez juste l'annotation @Autowired au-dessus des champs et c'est tout. Aucun constructeur ou setteur SPÉCIAL JUSTE POUR di container pour fournir vos dépendances. Java est très verbeux comme il est, donc chaque occasion de rendre votre code plus court 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'y a aucun problème à ajouter six, dix ou même douzaines de dépendances. Lorsque vous utilisez des constructeurs pour DI, après un certain point, le nombre de paramètres du 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 être une violation du principe de Responsabilité Unique et la séparation des préoccupations et est un bon indicateur, que la classe nécessite une inspection plus approfondie et peut-être le remaniement. Il n'y a pas de signal d'alarme de ce genre lorsque l'on s'injecte directement dans des champs, car cette approche peut prendre une échelle indéfinie.

Cacher Une Dépendance

par conteneur DI, on entend: que la classe n'est plus responsable de gérer ses propres dépendances. La responsabilité d'obtenir les dépendances est extraite de la classe. Une autre personne est maintenant responsable de fournir le conteneur dependencies - 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 les communiquer clairement en utilisant l'interface publique - les méthodes ou les constructeurs. De cette façon, il est clair ce que la classe exige et aussi si elle est facultatif (setters) ou obligatoire (constructors).

DI Conteneur de Couplage

L'une des idées de base des cadres DI est que la classe gérée ne devrait pas dépendre du conteneur DI utilisé. En d'autres termes, il devrait être juste un POJO simple, qui peut être instancié indépendamment, à condition que vous passiez toutes les dépendances requises. De cette façon, vous pouvez l'instancier dans un test unitaire sans démarrer le conteneur DI et tester séparément (avec un contenant plus de test d'intégration). S'il n'y a pas de couplage de conteneur, vous pouvez utiliser la classe comme managé ou non-managé ou même passer à un nouveau cadre DI.

cependant, lorsque vous injectez directement dans les champs, vous ne fournissez aucun moyen direct d'instancier la classe avec toutes ses dépendances requises. Cela signifie:

il y a un moyen (en appelant le constructeur par défaut) de créer un objet en utilisant new dans un précisez quand il manque certains de ses collaborateurs obligatoires et l'utilisation résultera en NullPointerException . Une telle classe ne peut pas être réutilisée à l'extérieur des conteneurs DI (tests, autres modules) car il n'y a pas d'autre moyen que la réflexion pour lui fournir les dépendances requises. Immutabilité Contrairement au constructeur, l'injection de champ ne peut pas être utilisée pour assigner des dépendances aux champs finaux rendant effectivement vos objets mutables.

1
répondu pramodgautam 2018-10-06 04:38:16