Ich kenne den Frühling nicht so gut und habe folgende Situation:
Eine Repository-Klasse:
@Repository
public class MyRepository {
// ...
}
Eine Klasse, die die Repository-Klasse verwendet:
public class MyClass extends AbstractClass {
@Autowired
private MyRepository myRepository;
//...
}
Ich weiß, dass, wenn ich meine MyClass
mit @Component
kommentiere und sie mit einem @Autowired
verwende, der @Autowired
MyRepository
in Ordnung ist. Das Problem ist, dass ich in der Lage bin, neue Instanzen von MyClass
mit Reflektion zu erstellen. MyRepository
wird also niemals aufgelöst und ist immer null.
Gibt es eine Möglichkeit, @Autowired
in dieser Situation zu verwenden?
Um meine Situation besser zu erklären: Ich habe einige Implementierungen von AbstractClass
. In einer Setup-Phase meiner Anwendung erstelle ich eine HashMap
dieser Implementierungen. Grundsätzlich gilt:
{"MyClass", MyClass.class}
//...
Dann habe ich ein generisches Controller
, das der URL /{class}?options=...
.__ zugeordnet ist. Mit dem {class}
@PathVariable
, der HashMap
und der Reflektion kann ich eine Instanz einer Klasse erstellen, die auf dem angegebenen options
basiert (dieser Teil ist wichtig). Glaubst du, es gibt einen besseren Weg, das zu tun?
Danke im Voraus
Spring selbst bietet einige Funktionen für die automatische Verdrahtung Ihrer Objekte .__, die Sie mit new
oder newInstance()
oder einem anderen Objekt erstellt haben.
Um es zu verwenden, benötigen Sie eine AutowireCapableBeanFactory
, Die Sie durch die normale Abhängigkeitseingabe von Spring mit @Autowired
erhalten.
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
Dann verwenden Sie seine autowireBean(Object)
method, um die @Autowired
-Eigenschaften in Ihre Bean einzuspeisen.
Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);
Design Hinweis:
Denken Sie gut darüber nach, wenn Sie wirklich den oben genannten Ansatz benötigen. Der Javadoc von AutowireCapableBeanFactory
rät von der Verwendung dieser Schnittstelle für die meisten Anwendungsfälle ab:
Diese Unterschnittstelle von BeanFactory ist nicht für die Verwendung in normalem Anwendungscode vorgesehen: Halten Sie in typischen Anwendungsfällen
BeanFactory
oderListableBeanFactory
fest.Integrationscode für andere Frameworks kann diese Schnittstelle nutzen, um vorhandene Bean-Instanzen zu verbinden und aufzufüllen, für die Spring den Lebenszyklus nicht steuert. Dies ist besonders nützlich, z. B. für WebWork-Aktionen und Tapestry-Seitenobjekte.
Eine Lösung besteht darin, MyClass nicht an die Hashmap zu binden, um eine Factory-Klasse zu binden. MyClassFactory. Auf diese Weise delegieren Sie die Konstruktion an eine konkrete Fabrik, die die Aufgabe übernimmt, die richtige Klasse zu instanziieren und das richtige Repository zu initialisieren.
Hier ist ein Beispiel:
{"MyClass", MyClassFactory.class}
Die Factory kann auch Component sein. Dann müssen Sie die Hashmap an die Factory-Instanz statt an die Factory-Klasse binden. Aber sagen wir mal, es ist keine Komponente:
//@Component this is optional
public MyClassFactory {
//@Autowired optional
ApplicationContext ctx;
public MyClass createInstance() {
MyRepository repo = ctx.getBean("")
MyClass myclass = new MyClass(repo)
return myclass;
}
}
Wenn Sie es als Komponente markieren, können Sie auch die ApplicationContextAware-Schnittstelle verwenden, wenn Sie ApplicationContext automatisch aktivieren möchten.
Sie können Factory Design Pattern hier verwenden.
Dies mag beim Start etwas kompliziert erscheinen, aber ich bin mir sicher, dass Sie es lieben werden, nachdem Sie es implementiert haben.
Schritte:
Erstellen Sie eine Factory-Klasse als:
@Component
public class MyFactory {
private final Map<String, AbstractClass> impletationMap = new HashMap<>();
@Autowired
ApplicationContext context;
@PostConstruct
public void initialize() {
populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
}
private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
while (classIterator.hasNext()) {
AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
}
}
}
Wenn das Bean dieser MyFactory-Klasse initialisiert wird, sucht es nach allen Beans vom Typ AbstractClass und fügt sie in die HashMap (ImplementierungsMap) ein.
Jetzt können Sie aus dieser Factory die HashMap und dann die Implementierungen erhalten, wann und wo Sie möchten. Es ist sehr einfach, wenn Sie eine neue Implementierung von AbstractClass hinzufügen, da Factory sich darum kümmert.
Versuche dies
@Component
public class SomeClass extends AbstractClass {
private static ApplicationContext applicationContext;
public MyClass getMyClass(){
// Now @Autowired MyRepository will work
return applicationContext.getBean(MyClass.class);
}
}
Ein Ansatz besteht darin, @Component
über MyClass
zu deklarieren.
In der Setup-Phase können Sie in der HashMap die instance von MyClass
anstelle von MyClass.class
selbst übergeben. Es ist nicht nötig, Instanzen über Reflection zu erstellen.
Hinweis: Sie können die Instanz von MyClass
von Ihrer ApplicationContext
in der Setup-Phase abrufen.
Ja, Sie können alle AbstractClass-Implementierungs-Beans mit @Component kommentieren und die nächste Deklaration verwenden
@Autowired
private List<AbstractClass> beans;
Sie können diese dann in einer @ PostConstruct-Methode in eine Map konvertieren.
Spring beschwert sich nicht über doppelte Definitionen, wenn Sie Listen automatisch erstellen.