webentwicklung-frage-antwort-db.com.de

Gibt es eine Möglichkeit, @Autowire eine Bean zu erstellen, die Konstruktorargumente erfordert?

Ich verwende Spring 3.0.5 und verwende @Autowire-Annotation für meine Schüler so weit wie möglich. Eine der Beans, die ich zum automatischen Drahtwechsel benötige, erfordert Argumente für ihren Konstruktor. Ich habe die Spring-Dokumente durchgesehen, finde aber keinen Hinweis darauf, wie Konstruktorargumente kommentiert werden.

In XML kann ich als Teil der Bean-Definition verwenden. Gibt es einen ähnlichen Mechanismus für die @Autowire-Anmerkung?

Ex:

@Component
public class MyConstructorClass{

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


@Service
public class MyBeanService{
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

Wie kann ich in diesem Beispiel den Wert von "constrArg" in MyBeanService mit der Annotation @Autowire angeben? Gibt es eine Möglichkeit, dies zu tun?

Vielen Dank,

Eric

83
Eric B.

Sie benötigen die Annotation @Value .

Ein häufiger Anwendungsfall ist das Zuweisen von Standardfeldwerten mithilfe von "#{systemProperties.myProp}" Stilausdrücke.

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;
  }

  // ...
}

See:Ausdruckssprache> Anmerkungskonfiguration


Um es klarer zu sagen: In Ihrem Szenario würden Sie zwei Klassen, MybeanService und MyConstructorClass, verbinden, etwa so:

@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
    }
}

Update: Wenn Sie mehrere verschiedene Instanzen von MyConstructorClass mit unterschiedlichen Werten benötigen, sollten Sie Qualifier-Annotationen verwenden

52

Wie gebe ich in diesem Beispiel den Wert von "constrArg" in MyBeanService mit der Annotation @Autowire an? Gibt es eine Möglichkeit, dies zu tun?

Nein, nicht so, wie Sie es meinen. Die Bean, die MyConstructorClass darstellt, muss konfigurierbar sein, ohne dass eine der Client-Beans erforderlich ist. Daher gibt MyBeanService keinen Einfluss darauf, wie MyConstructorClass konfiguriert ist.

Dies ist kein automatisches Problem, das Problem ist, wie Spring MyConstructorClass instanziiert, vorausgesetzt MyConstructorClass ist ein @Component (und Sie verwenden Komponentenscan und geben daher nicht explizit MyConstructorClass in Ihrer Konfiguration an).

Wie @Sean sagte, ist eine Antwort hier die Verwendung von @Value für den Konstruktorparameter, sodass Spring den Konstruktorwert aus einer Systemeigenschaft oder Eigenschaftendatei abruft. Die Alternative ist, MyBeanService direkt MyConstructorClass zu instanziieren, aber wenn Sie das tun, ist MyConstructorClass keine Spring-Bean mehr.

9
skaffman

Nun, ab und zu stoße ich auf die gleiche Frage. Soweit ich weiß, kann man das nicht tun, wenn man dem Konstruktor dynamische Parameter hinzufügen möchte. Das Fabrikmuster kann jedoch helfen.

public interface MyBean {
    // here be my fancy stuff
}

public interface MyBeanFactory {
    public MyBean getMyBean(/* bean parameters */);
}

@Component
public class MyBeanFactoryImpl implements MyBeanFactory {
    @Autowired
    WhateverIWantToInject somethingInjected;

    public MyBean getMyBean(/* params */) {
        return new MyBeanImpl(/* params */);
    }

    private class MyBeanImpl implements MyBean {
        public MyBeanImpl(/* params */) {
            // let's do whatever one has to
        }
    }
}

@Component
public class MyConsumerClass {
    @Autowired
    private MyBeanFactory beanFactory;

    public void myMethod() {
        // here one has to prepare the parameters
        MyBean bean = beanFactory.getMyBean(/* params */);
    }
}

Nun, MyBean ist an sich kein Spring Bean, aber nahe genug. Abhängigkeitsinjektion funktioniert, obwohl ich die Factory und nicht die Bean selbst injiziere - man muss eine neue Factory zusätzlich zu seiner eigenen neuen MyBean-Implementierung injizieren, wenn man sie ersetzen möchte.

Außerdem hat MyBean Zugriff auf andere Beans, da sie möglicherweise auf die automatisch verdrahteten Elemente der Fabrik zugreifen kann.

Und man möchte anscheinend der getMyBean-Funktion eine Logik hinzufügen, was ich mit mehr Aufwand erlaube, aber leider habe ich keine bessere Lösung. Da das Problem normalerweise darin besteht, dass die dynamischen Parameter aus einer externen Quelle stammen, wie z. B. einer Datenbank oder einer Benutzerinteraktion, muss ich diese Bean nur in der Zwischenzeit instanziieren, nur wenn diese Informationen leicht verfügbar sind. Daher sollte Factory ausreichen .

8
Ivan Ketler

Sie können Ihre Komponente auch so konfigurieren:

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


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

Mit der Annotation Bean weisen Sie Spring an, das zurückgegebene Bean in der Variable BeanFactory zu registrieren.

So können Sie es nach Belieben autowire machen.

3
Zakaria

Sie müssen @Autowired und @Value verwenden. Weitere Informationen zu diesem Thema finden Sie unter post .

1
Fahim Farook

Eine andere Alternative, wenn Sie bereits eine Instanz des Objekts erstellt haben und es als @autowired-Abhängigkeit hinzufügen möchten, um alle internen @autowired-Variablen zu initialisieren, könnte die folgende aussehen:

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

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

Eine Alternative wäre, die Parameter nicht an den Konstruktor zu übergeben, sondern als Getter und Setter zu verwenden und dann in einem @ PostConstruct die Werte wie gewünscht zu initialisieren. In diesem Fall erstellt Spring die Bean mit dem Standardkonstruktor. Ein Beispiel ist unten

@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

Die meisten Antworten sind ziemlich alt, so dass es damals vielleicht nicht möglich war, aber es gibt tatsächlich eine Lösung, die alle möglichen Anwendungsfälle erfüllt.

So richtig wissen die Antworten sind:

  • Keine echte Federkomponente (Werksausführung)
  • oder passt nicht in jede Situation (mit @Value müssen Sie den Wert irgendwo in einer Konfigurationsdatei haben)

Die Lösung zur Lösung dieser Probleme besteht darin, das Objekt manuell mit der Variablen ApplicationContext zu erstellen:

@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;
    }
}

Dies ist eine coole Lösung, weil:

  • Sie haben Zugriff auf alle Spring-Funktionen Ihres Objekts (offensichtlich @Autowired, aber auch @Async).
  • Sie können eine beliebige Quelle für Ihre Konstruktorargumente verwenden (Konfigurationsdatei, berechneter Wert, fest codierter Wert, ...),
  • Sie müssen lediglich einige Zeilen Code hinzufügen, ohne etwas ändern zu müssen.
  • Es kann auch verwendet werden, um eine unbekannte Anzahl von Instanzen einer von Spring verwalteten Klasse dynamisch zu erstellen (z. B. verwende ich zum Beispiel mehrere asynchrone Executoren).

Das einzige, was zu beachten ist, ist, dass Sie einen Konstruktor benötigen, der keine Argumente in der Klasse enthält, die Sie instanziieren möchten (oder einen @Autowired-Konstruktor, falls Sie ihn benötigen).

0
AntoineB