web-dev-qa-db-fra.com

Comment faire correctement l'injection de dépendances (au printemps)?

J'ai un doute sur l'injection d'objets dans une classe en utilisant Spring. J'ai utilisé dans mes projets ce type de code:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

puis l'utilisait normalement sur des méthodes comme:

personRepository.save(p);

Sinon, j'ai trouvé sur des exemples Spring, en injectant le constructeur:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

Les deux sont donc corrects? Ou chacun a ses propriétés et ses usages?

26
Joao Evangelista

tl; dr - L'injection de constructeur est la meilleure façon de faire DI

Ce dernier est correct et ce n'est pas tant à cause de Spring ou de tout conteneur d'injection de dépendance, mais des principes de conception de classe orientés objet.

Détails

Un type doit être conçu de manière à ce que vous ne puissiez en créer que des instances dans un état valide. Pour ce faire, toutes les dépendances obligatoires de ce type doivent être des arguments de constructeur. Cela signifie que ces dépendances peuvent être null- vérifiées, affectées aux champs finaux pour favoriser l'immuabilité. Au-delà de cela, lorsque vous travaillez avec le code, pour l'appelant (ou le créateur) de cette instance, il est immédiatement évident quelles dépendances il doit fournir (en parcourant les documents de l'API ou en utilisant la complétion de code dans l'IDE).

Tout cela est impossible avec l'injection sur le terrain. Vous ne voyez pas les dépendances de l'extérieur, vous avez besoin de magie noire pour injecter les dépendances et vous ne pouvez jamais être sûr qu'elles ne sont pas null sauf que vous faites aveuglément confiance au conteneur.

Le dernier aspect, mais non le moins important, est qu'avec les injections sur le terrain, il est moins pénible d'ajouter beaucoup de dépendances à votre classe, ce qui est un problème de conception en soi. Avec les constructeurs, cela devient beaucoup plus douloureux beaucoup plus tôt, ce qui est une bonne chose car cela vous dit quelque chose sur la conception de votre classe: la classe a trop de responsabilités. Pas besoin de calculer des métriques dessus, vous le ressentez lorsque vous essayez de l'étendre.

Conteneurs

Les gens soutiennent souvent que ce ne sont que des conneries académiques, car vous pouvez de toute façon compter sur le conteneur. Voici mon point de vue à ce sujet:

  • Ce n'est pas parce qu'un conteneur existe que vous devez jeter tous les principes de base de la conception orientée objet par dessus bord, n'est-ce pas? Vous prenez toujours une douche, même s'il existe des antisudorifiques, non?

  • Même les types conçus pour une utilisation avec un conteneur, seront utilisés manuellement: dans les tests unitaires. Si vous n'écrivez pas de tests unitaires… eh bien, c'est un autre sujet.

  • La prétendue verbosité d'un constructeur supplémentaire ("Je peux réaliser la même chose avec une seule ligne d'injection de champ !!" - "Non, vous ne pouvez pas. En fait vous obtenez les trucs de l'écriture d'une ligne de code plus. ") peuvent être atténués par des choses comme Lombok . Un composant injecté par le constructeur avec Spring et Lombok ressemble à ceci:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok se chargera de générer un constructeur prenant un paramètre pour chaque champ final et vérifiera le paramètre donné pour null avant de l'affecter. Ainsi, vous obtenez efficacement la brièveté de l'injection sur site et les avantages de conception de l'injection constructeur.

Épilogue

J'ai fait partie d'une discussion récemment avec des gens non Java que j'ai terriblement perplexe en utilisant le terme "injection" pour le constructeur DI. En fait, ils ont fait valoir - et il y a beaucoup de vérité à cela - que le fait de passer des dépendances via un constructeur n'est pas du tout une injection, car c'est la façon la plus naturelle de remettre des objets à d'autres (contrairement à des injections de toutes sortes).

Peut-être devrions-nous inventer un terme différent pour ce genre de style? L'alimentation de dépendance, peut-être?

Ressources

72
Oliver Drotbohm

D'un point de vue académique, je suis d'accord que les constructeurs sont simplement une meilleure façon de créer des objets. Cependant, la spécification Java beans est construite sur les prémisses des mutateurs pour faciliter la réflexion. Trop d'outils et de cadres ont été construits autour de ce paradigme de facilité d'accès. Pour les services, les DAO et autres scénarios singleton je crois seule l'injection de constructeur doit être utilisée car les mutateurs enfreignent la règle séculaire "seuls les amis peuvent voir vos parties intimes".

2
Ryan McGuinness