web-dev-qa-db-fra.com

Y at-il un moyen de @Autowire un bean qui nécessite des arguments de constructeur?

J'utilise Spring 3.0.5 et j'utilise l'annotation @Autowire pour mes membres de classe autant que possible. L'un des beans dont j'ai besoin pour autowire nécessite des arguments à son constructeur. J'ai parcouru les docs de Spring, mais je n'arrive pas à trouver de référence sur la façon d'annoter les arguments du constructeur.

En XML, je peux utiliser dans le cadre de la définition du bean. Existe-t-il un mécanisme similaire pour l'annotation @Autowire?

Ex:

@Component
public class MyConstructorClass{

  String var;
  public MyConstructorClass( String constrArg ){
    this.var = var;
  }
...
}


@Service
public class MyBeanService{
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

Dans cet exemple, comment spécifier la valeur de "constrArg" dans MyBeanService avec l'annotation @Autowire? Y a-t-il un moyen de faire ça?

Merci,

Eric

83
Eric B.

Vous avez besoin de l'annotation @Value .

Un cas d'utilisation courant consiste à attribuer des valeurs de champ par défaut à l'aide de expressions de style "#{systemProperties.myProp}".

public class SimpleMovieLister {

  private MovieFinder movieFinder;
  private String defaultLocale;

  @Autowired
  public void configure(MovieFinder movieFinder, 
                        @Value("#{ systemProperties['user.region'] }"} String defaultLocale) {
      this.movieFinder = movieFinder;
      this.defaultLocale = defaultLocale;
  }

  // ...
}

Voir:Langage d'expression> Configuration d'annotation


Pour être plus clair: dans votre scénario, vous câblez deux classes, MybeanService et MyConstructorClass, à peu près comme ceci:

@Component
public class MyBeanService implements BeanService{
    @Autowired
    public MybeanService(MyConstructorClass foo){
        // do something with foo
    }
}

@Component
public class MyConstructorClass{
    public MyConstructorClass(@Value("#{some expression here}") String value){
         // do something with value
    }
}

Mise à jour: si vous avez besoin de plusieurs instances différentes de MyConstructorClass avec des valeurs différentes, vous devez utiliser les annotations de qualificateur

52
Sean Patrick Floyd

Dans cet exemple, comment spécifier la valeur de "constrArg" dans MyBeanService avec l'annotation @Autowire? Y a-t-il un moyen de faire ça?

Non, pas comme tu veux dire. Le bean représentant MyConstructorClass doit être configurable sans nécessiter aucun de ses beans client. Par conséquent, MyBeanService n'a pas son mot à dire sur la configuration de MyConstructorClass.

Ce n'est pas un problème de câblage automatique, le problème ici est de savoir comment Spring instancie MyConstructorClass, étant donné que MyConstructorClass est un @Component (et que vous utilisez l'analyse de composants et que vous ne spécifiez donc pas explicitement une MyConstructorClass dans votre configuration).

Comme @Sean l'a dit, une solution consiste à utiliser @Value dans le paramètre constructeur, de sorte que Spring récupère la valeur du constructeur à partir d'une propriété système ou d'un fichier de propriétés. L'alternative est que MyBeanService instancie directement MyConstructorClass, mais si vous faites cela, alors MyConstructorClass n'est plus un bean Spring.

9
skaffman

Vous pouvez également configurer votre composant comme ceci:

package mypackage;
import org.springframework.context.annotation.Configuration;
   @Configuration
   public class MyConstructorClassConfig {


   @Bean
   public MyConstructorClass myConstructorClass(){
      return new myConstructorClass("foobar");
   }
  }
}

Avec l'annotation Bean, vous indiquez à Spring d'enregistrer le bean renvoyé dans BeanFactory.

Ainsi, vous pouvez le faire comme vous le souhaitez.

3
Zakaria

Vous devez utiliser @Autowired et @Value. Référez-vous à ce post pour plus d’informations sur ce sujet.

1
Fahim Farook

Une autre alternative, si vous avez déjà une instance de l'objet créée et que vous souhaitez l'ajouter en tant que dépendance @autowired pour initialiser toutes les variables internes @autowired, pourrait être la suivante:

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}
0
Jason Glez

Une alternative serait au lieu de transmettre les paramètres au constructeur, vous pourriez les avoir comme getter et setters puis, dans un @PostConstruct, initialiser les valeurs à votre guise. Dans ce cas, Spring créera le bean en utilisant le constructeur par défaut. Un exemple est ci-dessous

@Component
public class MyConstructorClass{

  String var;

  public void setVar(String var){
     this.var = var;
  }

  public void getVar(){
    return var;
  }

  @PostConstruct
  public void init(){
     setVar("var");
  }
...
}


@Service
public class MyBeanService{
  //field autowiring
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}
0
george

La plupart des réponses étant assez anciennes, cela n’a peut-être pas été possible à l’époque, mais il existe en fait une solution qui satisfait tous les cas d’utilisation possibles.

Alors connaissez les réponses:

  • Ne pas fournir un vrai composant Spring (la conception de l'usine)
  • ou ne convient pas à toutes les situations (avec @Value vous devez avoir la valeur dans un fichier de configuration quelque part)

La solution pour résoudre ces problèmes consiste à créer l'objet manuellement à l'aide de la variable ApplicationContext:

@Component
public class MyConstructorClass
{
    String var;

    public MyConstructorClass() {}
    public MyConstructorClass(String constrArg) {
        this.var = var;
    }
}

@Service
public class MyBeanService implements ApplicationContextAware
{
    private static ApplicationContext applicationContext;

    MyConstructorClass myConstructorClass;

    public MyBeanService()
    {
        // Creating the object manually
        MyConstructorClass myObject = new MyConstructorClass("hello world");
        // Initializing the object as a Spring component
        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(myObject);
        factory.initializeBean(myObject, myObject.getClass().getSimpleName());
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }
}

C'est une solution intéressante pour les raisons suivantes:

  • Il vous donne accès à toutes les fonctionnalités de Spring sur votre objet (@Autowired évidemment, mais aussi @Async par exemple),
  • Vous pouvez utiliser n’importe quelle source pour vos arguments de constructeur (fichier de configuration, valeur calculée, valeur codée en dur, ...),
  • Il vous suffit d'ajouter quelques lignes de code sans rien changer.
  • Il peut également être utilisé pour créer dynamiquement un nombre inconnu d'instances d'une classe gérée par Spring (je l'utilise pour créer plusieurs exécuteurs asynchrones à la volée, par exemple).

La seule chose à garder à l'esprit est que vous devez avoir un constructeur qui ne prend aucun argument (et qui peut être vide) dans la classe que vous voulez instancier (ou un constructeur @Autowired si vous en avez besoin).

0
AntoineB