web-dev-qa-db-fra.com

Comment définir les paramètres du constructeur @Autowired sur "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;    
   }
}

En tant que Spring documentation sais , je suis en mesure de déclarer si la dépendance annotée est requise. 

Si je marque l'annotation @Autowired sous le constructeur comme étant required=false, je dis que les deux services à autoriser automatiquement ne sont pas obligatoires (comme le dit la documentation Spring):

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

Documentation de Spring:

Dans le cas de méthodes à arguments multiples, le paramètre "requis" est applicable pour tous les arguments.

Comment définir l'attribut required sur chaque paramètre de constructeur individuellement? Est-il nécessaire d'utiliser l'annotation @Autowired sous chaque champ?

Cordialement,

24
jcgarcia

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});   
}
31
Strelok

Approche explicite

En gros, vous avez un haricot qui contient des dépendances obligatoires et facultatives. La méthode recommandée pour gérer ce scénario, non seulement les beans de configuration, mais tous les autres, consiste à créer un constructeur uniquement pour les dépendances obligatoires et à utiliser l'injection de setter pour les 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 la classe à l’unité sans avoir besoin d’une bibliothèque moqueuse. Vous pouvez créer un objet à l'état de test à l'aide du constructeur et de paramètres optionnels.

Placer @Autowired(required = false) directement sur le champ et supprimer le setter fonctionnera également, mais comme vous utilisez l'injection de constructeur, je suppose que vous souhaitez énoncer plus explicitement les dépendances.

Idée additionnelle

Vous pouvez également envisager d'utiliser le type facultatif pour envelopper les dépendances non obligatoires. Il est courant que les développeurs supposent que si une classe a une propriété, elle devrait être définie, ce qui, de toute évidence, ne convient pas à votre scénario. Pour marquer plus clairement la possibilité d'absence pour des dépendances particulières, vous pouvez probablement utiliser Facultatif:

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 ce réponse de Brian Goetz ), mais au bout du compte, ce devrait être la décision prise par toute l'équipe de travailler sur le projet.

21
Daniel Olszewski

Depuis le printemps 4.3.0.RC1, vous pouvez faire ceci:

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

Comme ElementType.PARAMETER a été ajouté comme cible d'annotation.

6
Czar

A partir de Spring Framework 5.0, vous pouvez également utiliser une annotation @Nullable (de tout type dans n'importe quel package, par exemple javax.annotation.Nullable à partir de JSR-305):

@Configuration
public class MyConfiguration {

   private MyServiceA myServiceA;
   private MyServiceB myServiceB

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