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
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
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.
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 .
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.
Sie müssen @Autowired und @Value verwenden. Weitere Informationen zu diesem Thema finden Sie unter post .
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);
}
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;
....
}
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:
@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:
@Autowired
, aber auch @Async
).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).