web-dev-qa-db-fra.com

Expliquer pourquoi constructeur inject est meilleur que les autres options

Dans un livre de Spring 3, chapitre 4 - Introduction IOC et DI dans Spring - Page 59, dans la section "Injection de fixeur par rapport à une injection de constructeur", un paragraphe indique

Spring inclus, fournit un mécanisme permettant de s'assurer que toutes les dépendances sont définies lorsque vous utilisez Setter Injection, mais en utilisant Constructor Injection, vous affirmez l'exigence de dépendance dépendante du conteneur. "

Pourriez-vous expliquer avec des exemples

61
minil

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

Si vous utilisez l'injection de setter, le setter peut être appelé ou non, de sorte que l'instance peut ne jamais être fournie avec sa dépendance. Le seul moyen de forcer l'appeleur à appeler est d'utiliser @Required ou @Autowired, qui est spécifique à Spring et n’est donc pas agnostique au conteneur.

Donc, pour que votre code reste indépendant de Spring, utilisez les arguments du constructeur pour l'injection.

Mise à jour : Spring 4.3 exécutera injection implicite dans des scénarios à constructeur unique , ce qui rend votre code plus indépendant de Spring en ne nécessitant potentiellement pas un @Autowired annotation du tout.

79
Emerson Farrugia

(...) en utilisant Constructor Injection, vous affirmez l'exigence relative à la dépendance de manière indépendante du conteneur.

Cela signifie que vous pouvez imposer des exigences pour tous les champs injectés sans utiliser de solution spécifique à un conteneur.


Exemple d'injection de poseur

Avec annotation spéciale de ressort d'injection de setter @Required est requis.

@ Obligatoire

Marque une méthode (généralement une méthode de définition JavaBean) comme étant 'requis': en d'autres termes, la méthode de définition doit être configurée pour être injectée avec une valeur avec une dépendance.

tilisation

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 obligatoires sont définis dans le constructeur, pure Java solution.

tilisation

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class Foo {

    private Bar bar;

    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }

}
16
MariuszS

Pour simplifier les choses, supposons que nous puissions utiliser l’injection de dépendance 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, faites-le toujours avec son constructeur. Donc, si vous utilisez une injection basée sur le constructeur, le seul moyen d'instancier la classe consiste à utiliser ce constructeur. Si vous passez la dépendance par le constructeur, il devient évident que c'est une dépendance obligatoire.

D'autre part, si vous avez une méthode setter dans une classe POJO, vous pouvez ou non définir la valeur de votre variable de classe à l'aide de cette méthode setter. C'est complètement basé sur vos besoins. c'est-à-dire que c'est optionnel. 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 optionnelle. J'espère que c'est clair !!

11
Jawa

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

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

A titre d'exemple concret, considérons un ServiceRepository qui dépend d'IService pour effectuer son travail. Etant donné que 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 Property injection.

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és.

5
vijayst

En utilisant Constructor Injection, vous affirmez l'exigence de la dépendance de manière agnostique par conteneur

Le conteneur IoC doit garantir que, avant d’utiliser un haricot, l’injection des haricots nécessaires doit être effectuée.

Dans la stratégie injection de setter, nous croyons que le conteneur IoC créera d’abord le bean mais fera l’injection juste avant de l’utiliser à l’aide des méthodes de setter. Et l'injection se fait en fonction de votre configuration. Si, d'une manière ou d'une autre, vous omettez de spécifier des haricots à injecter dans la configuration, l'injection ne sera pas effectuée pour ces haricots et votre haricot dépendant ne fonctionnera pas en conséquence lorsqu'il sera utilisé!

Mais dans la stratégie injection de constructeur, le conteneur impose (ou doit imposer) de fournir les dépendances correctement lors de la construction du bean. Ceci a été traité comme "de manière agnostique par conteneur" , car nous sommes obligés de fournir des dépendances lors de la création du bean, ce qui rend la visibilité de la dépendance, indépendante de tout conteneur IoC.

Modifier:

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

Vous n'avez aucune option pour rater vraiment aucun <constructor-arg> _ (dans le cas de Spring), car le conteneur IoC vous impose de fournir tous les arguments de constructeur nécessaires pour faire correspondre un constructeur fourni pour la création du bean. Si vous fournissez null dans votre <constructor-arg> intentionnellement. Ensuite, il n'y a rien que le conteneur IoC puisse faire ou avoir besoin de faire avec!

4
Sazzadur Rahaman

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 String 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 de l'interface:

public interface Color {
    public String getName();
}

Classe Rouge:

@Component
public class Red implements Color{
private String name;

@Override
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Red(String name) {
    System.out.println("Red color: "+ name);
    this.name = name;
}

public Red() {
    System.out.println("Red color default constructor");
}

}

Classe Black:

@Component
public class Black implements Color{
private String name;

@Override
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Black(String 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 haricots:

@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(String[] args) {
    SpringApplication.run(BootApplication.class, args);
}
}

Lancer l'application et cliquer 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
3
quintin

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 de constructeur quand je peux.

Dans les deux cas, la fabrique de beans instancie les instances TwoInjectionStyles et Foo et lui donne sa dépendance Foo.

0
duffymo