Comment définir @Autowired constructor params comme" required=false " individuellement

j'utilise l'annotation @Autowired sous un constructeur de classe @Configuration .

@Configuration
public class MyConfiguration {

   private MyServiceA myServiceA;
   private MyServiceB myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA, MyServiceB myServiceB){
     this.myServiceA = myServiceA;
     this.myServiceB = myServiceB;    
   }
}

comme la documentation de ressort est , je suis en mesure de déclarer si la dépendance annotée est nécessaire.

si je marque l'annotation @Autowired sous le constructeur comme required=false , je dis que les deux services à être autowired ne sont pas requis (comme la documentation de ressort dit) :

@Autowired(required = false)
public MyConfiguration(MyServiceA myServiceA, MyServiceB myServiceB){
  this.myServiceA = myServiceA;
  this.myServiceB = myServiceB;   
}

à Partir du Printemps de la documentation:

dans le cas de méthodes à arguments multiples, le paramètre 'required' est applicable à tous les arguments.

comment définir l'attribut required à chaque paramètre constructeur individuellement? Est-il nécessaire d'utiliser l'annotation @Autowired dans chaque champ?

concerne",

20
demandé sur jcgarcia 2017-02-09 14:10:24

3 réponses

si vous utilisez Java 8 et Spring Framework 4, Vous pouvez utiliser Optional .

@Autowired
public MyConfiguration(Optional<MyServiceA> myServiceA, Optional<MyServiceB> myServiceB){
  myServiceA.ifPresent(service->{this.myServiceA = service});
  myServiceB.ifPresent(service->{this.myServiceB = service});   
}
25
répondu Strelok 2017-02-09 11:24:53

approche explicite

fondamentalement, vous avez un haricot qui ont quelques dépendances requises et optionnelles. La manière recommandée de gérer ce scénario, pas seulement la configuration, mais toute autre, est de créer un constructeur uniquement pour les dépendances obligatoires et d'utiliser setter injection pour les dépendances optionnelles.

public class MyConfiguration {

   private final MyServiceA myServiceA;
   private MyServiceB myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA){
     this.myServiceA = myServiceA;   
   }

   @Autowired(required = false)
   public void setMyServiceB(MyServiceB myServiceB) {
     this.myServiceB = myServiceB;
   }

}

avec cette approche, vous pouvez facilement tester l'Unité de la classe sans nécessité pour toute bibliothèque moqueuse. Vous pouvez créer un objet dans l'état testing en utilisant le constructeur et les setters optionnels.

mettre @Autowired(required = false) directement sur le terrain et supprimer le setter fonctionnera aussi, mais puisque vous utilisez l'injection du constructeur, je suppose que vous voulez énoncer les dépendances plus explicitement.

idée supplémentaire

vous pouvez également envisager d'utiliser le type optionnel pour envelopper des dépendances non obligatoires. Il est commun parmi les développeurs de supposer que si une classe a une propriété, il doit être mis, ce qui n'est évidemment pas juste dans votre scénario. Pour marquer plus clairement la possibilité d'absence pour des dépendances particulières, vous pouvez probablement utiliser Optional:

public class MyConfiguration {

   private final MyServiceA myServiceA;
   private Optional<MyServiceB> myServiceB

   @Autowired
   public MyConfiguration(MyServiceA myServiceA){
     this.myServiceA = myServiceA;
     this.myServiceB = Optional.empty();   
   }

   @Autowired(required = false)
   public void setMyServiceB(MyServiceB myServiceB) {
     this.myServiceB = Optional.ofNullable(myServiceB);
   }

}

certaines personnes sont contre l'utilisation du type Optional pour les propriétés de classe (principalement à cause de cette réponse de Brian Goetz ), mais à la fin de la journée, ce devrait être la décision prise par toute l'équipe qui va travailler sur le projet.

20
répondu Daniel Olszewski 2017-05-23 12:09:21

Depuis Le Printemps 4.3.0.RC1 vous pouvez le faire:

public MyConfiguration(MyServiceA myServiceA, @Autowired(required = false) MyServiceB myServiceB){
  this.myServiceA = myServiceA;
  this.myServiceB = myServiceB;   
}

comme ElementType.PARAMETER a été ajouté comme objectif d'annotation.

3
répondu Czar 2018-05-14 13:59:32