Expliquer pourquoi le constructeur injecter est meilleur que les autres options

Dans un livre Pro Spring 3, chapitre 4 – Introduction IOC et DI au spring – page 59, dans la section “Injection Setter vs. Injection constructeur”, un paragraphe dit:

Spring inclus, fournit un mécanisme pour garantir que toutes les dépendances sont définies lorsque vous utilisez l’injection Setter, mais en utilisant Constructor Injection, vous imposez la dépendance de manière indépendante du conteneur ”

Pourriez-vous expliquer avec des exemples

Une classe qui prend une dépendance requirejse en tant qu’argument constructeur ne peut être instanciée que si cet argument est fourni (vous devez avoir une clause guard pour vous assurer que l’argument n’est pas null). Un constructeur applique donc l’exigence de dépendance, que vous soyez ou non en utilisant Spring, le rendant agnostique aux conteneurs.

Si vous utilisez l’injection setter, le composeur peut ou peut ne pas être appelé, ainsi l’instance peut ne jamais être fournie avec sa dépendance. La seule façon de forcer l’appel du @Required consiste à utiliser @Required ou @Autowired , qui est spécifique à Spring et n’est donc pas agnostique pour les conteneurs.

Donc, pour garder votre code indépendant de Spring, utilisez des arguments de constructeur pour l’injection.

Mise à jour : Spring 4.3 effectuera une injection implicite dans des scénarios à constructeur unique , rendant votre code plus indépendant de Spring en ne nécessitant @Autowired annotation @Autowired .

(…) en utilisant Constructor Injection, vous affirmez la nécessité de la dépendance de manière indépendante du conteneur

Cela signifie que vous pouvez appliquer les exigences pour tous les champs injectés sans utiliser de solution spécifique au conteneur .


Exemple d’injection de poseur

Avec l’injection de setter, une annotation de ressort spéciale @Required est requirejse.

@Champs obligatoires

Marque une méthode (généralement une méthode de réglage JavaBean) comme étant « obligatoire »: la méthode de définition doit être configurée pour être injectée avec une valeur.

Usage

 import org.springframework.beans.factory.annotation.Required; import javax.inject.Inject; import javax.inject.Named; @Named public class Foo { private Bar bar; @Inject @Required public void setBar(Bar bar) { this.bar = bar; } } 

Exemple d’injection de constructeur

Tous les champs requirejs sont définis dans constructeur, solution Java pure.

Usage

 import javax.inject.Inject; import javax.inject.Named; @Named public class Foo { private Bar bar; @Inject public Foo(Bar bar) { this.bar = bar; } } 
  1. Dépendance partielle: peut être injecté en utilisant l’injection de setter mais ce n’est pas possible par constructeur. Supposons qu’il y ait 3 propriétés dans une classe, ayant 3 méthodes de constructeur et de paramètre arg. Dans ce cas, si vous souhaitez transmettre des informations pour une seule propriété, cela est possible uniquement par la méthode setter.
  2. Remplacement: L’ injection de Setter remplace l’injection du constructeur. Si nous utilisons à la fois l’injection du constructeur et celle du setter, le conteneur IOC utilisera l’injection du setter.
  3. Changements: Nous pouvons facilement changer la valeur par injection de setter. Il ne crée pas une nouvelle instance de bean toujours comme un constructeur. L’injection de setter est donc flexible que l’injection de constructeur

Pour simplifier, disons que nous pouvons utiliser l’dependency injection basée sur le constructeur pour les dépendances obligatoires et l’injection basée sur le setter pour les dépendances facultatives. C’est une règle de base !!

Disons par exemple.

Si vous voulez instancier une classe, vous le faites toujours avec son constructeur. Donc, si vous utilisez l’injection basée sur le constructeur, le seul moyen d’instancier la classe passe par ce constructeur. Si vous transmettez la dépendance via le constructeur, il devient évident qu’il s’agit d’une dépendance obligatoire.

D’un autre côté, si vous avez une méthode setter dans une classe POJO, vous pouvez ou non définir la valeur de votre variable de classe en utilisant cette méthode setter. Il est entièrement basé sur vos besoins. c’est à dire que c’est facultatif. Donc, si vous passez la dépendance via la méthode setter d’une classe, cela signifie implicitement qu’il s’agit d’une dépendance facultative. J’espère que c’est clair !!

L’injection de constructeur est utilisée lorsque la classe ne peut pas fonctionner sans la classe dépendante.

La propriété injection est utilisée lorsque la classe peut fonctionner sans la classe dépendante.

Comme exemple concret, considérons un ServiceRepository qui dépend de IService pour faire son travail. Puisque ServiceRepository ne peut pas fonctionner utilement sans IService, il est logique de l’injecter via le constructeur.

La même classe ServiceRepository peut utiliser un enregistreur pour effectuer le traçage. ILogger peut être injecté via injection de propriété.

ICache (un autre aspect de la terminologie AOP) ou IBaseProperty (une propriété de la classe de base) sont d’autres exemples courants d’injection de propriété.

Cet exemple peut aider:

Classe de contrôleur:

 @RestController @RequestMapping("/abc/dev") @Scope(value = WebApplicationContext.SCOPE_REQUEST) public class MyController { //Setter Injection @Resource(name="configBlack") public void setColor(Color c) { System.out.println("Injecting setter"); this.blackColor = c; } public Color getColor() { return this.blackColor; } public MyController() { super(); } Color nred; Color nblack; //Constructor injection @Autowired public MyController(@Qualifier("constBlack")Color b, @Qualifier("constRed")Color r) { this.nred = r; this.nblack = b; } private Color blackColor; //Field injection @Autowired private Color black; //Field injection @Resource(name="configRed") private Color red; @RequestMapping(value = "/customers", produces = { "application/text" }, method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.CREATED) public Ssortingng createCustomer() { System.out.println("Field injection red: " + red.getName()); System.out.println("Field injection: " + black.getName()); System.out.println("Setter injection black: " + blackColor.getName()); System.out.println("Constructor inject nred: " + nred.getName()); System.out.println("Constructor inject nblack: " + nblack.getName()); MyController mc = new MyController(); mc.setColor(new Red("No injection red")); System.out.println("No injection : " + mc.getColor().getName()); return "Hello"; } } 

Couleur d’interface:

 public interface Color { public Ssortingng getName(); } 

Classe rouge:

 @Component public class Red implements Color{ private Ssortingng name; @Override public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } public Red(Ssortingng name) { System.out.println("Red color: "+ name); this.name = name; } public Red() { System.out.println("Red color default constructor"); } } 

Classe Noir:

 @Component public class Black implements Color{ private Ssortingng name; @Override public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } public Black(Ssortingng name) { System.out.println("Black color: "+ name); this.name = name; } public Black() { System.out.println("Black color default constructor"); } } 

Classe de configuration pour la création de beans:

 @Configuration public class Config { @Bean(name = "configRed") public Red getRedInstance() { Red red = new Red(); red.setName("Config red"); return red; } @Bean(name = "configBlack") public Black getBlackInstance() { Black black = new Black(); black.setName("config Black"); return black; } @Bean(name = "constRed") public Red getConstRedInstance() { Red red = new Red(); red.setName("Config const red"); return red; } @Bean(name = "constBlack") public Black getConstBlackInstance() { Black black = new Black(); black.setName("config const Black"); return black; } } 

BootApplication (classe principale):

 @SpringBootApplication @ComponentScan(basePackages = {"com"}) public class BootApplication { public static void main(Ssortingng[] args) { SpringApplication.run(BootApplication.class, args); } } 

Exécutez l’application et cliquez sur l’URL: GET 127.0.0.1:8080/abc/dev/customers/

 Output: Injecting setter Field injection red: Config red Field injection: null Setter injection black: config Black Constructor inject nred: Config const red Constructor inject nblack: config const Black Red color: No injection red Injecting setter No injection : No injection red 

En utilisant Constructor Injection, vous affirmez la nécessité de la dépendance de manière indépendante du conteneur

Nous avons besoin de l’assurance du conteneur de l’IoC pour que, avant d’utiliser un haricot, l’injection des grains nécessaires soit effectuée.

Dans la stratégie d’ injection de setter , nous faisons confiance au conteneur IoC qui créera d’abord le bean mais effectuera l’injection juste avant d’utiliser le bean en utilisant les méthodes setter. Et l’injection se fait selon votre configuration. Si vous ne savez pas comment spécifier des beans à injecter dans la configuration, l’injection ne sera pas effectuée pour ces beans et votre bean dépendant ne fonctionnera pas en conséquence!

Mais dans la stratégie d’ injection de constructeur , le conteneur impose (ou doit imposer) fournir correctement les dépendances lors de la construction du bean. Cela a été abordé comme méthode indépendante du conteneur , car nous sums tenus de fournir des dépendances lors de la création du bean, rendant ainsi la visibilité de la dépendance indépendante de tout conteneur IoC.

Modifier:

Q1: Et comment empêcher le conteneur de créer un bean par constructeur avec null valeurs null au lieu de beans manquants?

Vous n’avez aucune option pour manquer vraiment un (dans le cas de Spring), car le conteneur IoC vous impose de fournir tous les arguments de constructeur nécessaires pour correspondre à un constructeur fourni pour créer le bean. Si vous indiquez intentionnellement null dans votre . Alors, il n’y a rien que le conteneur IoC puisse faire ou avoir besoin de faire avec!

Avec des exemples? Voici un simple:

 public class TwoInjectionStyles { private Foo foo; // Constructor injection public TwoInjectionStyles(Foo f) { this.foo = f; } // Setting injection public void setFoo(Foo f) { this.foo = f; } } 

Personnellement, je préfère l’injection constructeur quand je peux.

Dans les deux cas, la fabrique de bean instancie les instances TwoInjectionStyles et Foo et donne à la première sa dépendance Foo .