web-dev-qa-db-fra.com

utiliser @autowired dans la classe de base abstraite

Comme je le sais, field injection n'est pas recommandé. Doit utiliser constructor à la place.

Ce que j'essaie de faire ici, c'est d'utiliser @Autowired dans le constructeur de la classe de base, et la rendre accessible à toutes les sous-classes. Dans certaines sous-classes, j'ai également besoin de certains beans spécifiques pour être @Autowired de leurs constructeurs. Le code de démonstration est le suivant:

Classe de base:

public abstract class Base {

    protected final MyDemoService myDemoService;

    @Autowired
    public Base(MyDemoService myDemoService) {
        this.myDemoService = myDemoService;
    }
}

Classe (sous) héritée:

public class Sub extends Base {

    private final MySubService mySubService;

    @Autowired
    public Sub(MySubService mySubService) {
        this.mySubService = mySubService;
    }
} 

Cela me donnera une erreur "pas de constructeur par défaut".
C'est similaire à la question: question et réponse similaires , mais cela ne fonctionne pas ici.


Je m'y suis plongé depuis un moment, j'ai trouvé cet article sur dependency injection: lire la suite

Je pense que c'est le Setter Injection un bon moyen pour ma question?

Setter injection:

public abstract class Base {

    protected MyDemoService myDemoService;

    @Autowired
    public void setMyDemoService(MyDemoService myDemoService) {
        this.myDemoService = myDemoService;
    }
}

Je suis nouveau à Java Spring Boot, et je veux obtenir des conseils d'expertise de votre part. Toute discussion sera très appréciée!

14
Vigor

Le code que vous fournissez ne sera pas compilé. Tant que dans votre classe de base vous n'avez pas le constructeur par défaut, vous devez appeler super(MyDemoService) dans child.

Classe (sous) héritée:

public class Sub extends Base {

    private final MySubService mySubService;

    @Autowired
    public Sub(MySubService mySubService, MyDemoService myDemoService){
        super(myDemoService);
        this.mySubService = mySubService;
    }
} 

Ou, si MySubService est une implémentation de MyDemoService

@Autowired
public Sub(MySubService mySubService){
    super(mySubService);
}

Tant que votre domaine MyDemoService myDemoService dans la classe abstraite est protected vous pouvez l'utiliser dans des sous-classes.

Si vous avez plusieurs implémentations de MyDemoService, vous devez utiliser @Qualifier comme décrit dans réponse que vous avez mentionnée .

public Sub(@Qualifier("MySubService") MyDemoService mySubService){
    super(mySubService);
}
6
Sergii Bishyr

N'utilisez pas l'injection de champs, utilisez l'injection de constructeur en rappelant les super constructeurs selon le cas.

L'injection du constructeur garantit que votre objet est correctement rempli avant l'instanciation, l'injection de setter ne le fait pas et rend le code plus sujet aux bogues (pas un autre bogue nullpointer ....)

Si vous êtes préoccupé par le code supplémentaire que vous devez écrire, utilisez Project Lombok pour laisser Lombok générer le code constructeur pour vous comme décrit ici Pourquoi l'injection de champs est mauvaise

Soit dit en passant à partir du printemps 4, si vous n'avez qu'un seul constructeur dans votre classe, vous n'avez pas besoin de @Autowired sur le constructeur.

3
PaulNUK

Je suggère d'utiliser @Configuration Des classes. De cette façon, vous pouvez supprimer entièrement les annotations Spring de vos classes professionnelles:

public class Sub extends Base {

    private final MySubService mySubService;

    public Sub(MySubService mySubService, MyDemoService myDemoService){
        super(myDemoService);
        this.mySubService = mySubService;
    }
} 

Configuration de printemps:

@Configuration
public class SubConfig{

    @Bean
    public Sub sub(MySubService subService, MyDemoService demoService){
        return new Sub(subService, demoService);
    }
}

Avec les classes de configuration, vous ne comptez plus sur l'analyse de chemin de classe magique, mais instanciez à nouveau manuellement les beans. Il y a beaucoup moins de surprises dans cette approche.

2
Sean Patrick Floyd

Ça marche!!!! Constructeur Autowired Qualifier Inheritance Injection

Classe abstraite de base DocumentoBase.

@Qualifier("DocumentServiceBase")
private DocumentService documentService;

@Autowired
public DocumentoBase(@Qualifier("DocumentServiceBase") DocumentService documentService){
    this.documentService = documentService;
}

Classe héritée DocumentoController.

@Qualifier("DocumentServiceImplv13")
private DocumentService documentService;

@Autowired
public DocumentoController(@Qualifier("DocumentServiceImplv13") DocumentService documentService){
    super(documentService);
    this.documentService = documentService;
}
1