Ich habe eine Schnittstelle namens MyInterface
. Die Klasse, die MyInterface
implementiert (wir nennen sie MyImplClass
), implementiert auch die Schnittstelle Runnable
, sodass ich sie zum Instanziieren von Threads verwenden kann. Das ist jetzt mein Code.
Was ich tun möchte, ist, die implementierende Klasse in meiner ApplicationContext.xml zu deklarieren und für jede Iteration eine neue Instanz zu bekommen. Also sieht mein Code ungefähr so aus:
%Vor% Ich möchte immer noch das IoC-Muster beibehalten.
Wie kann ich das tun?
Danke
Sie können das Fabrikmuster mit dem Prototyp des Federumfangs wie unten versuchen. Definieren Sie eine abstrakte Factory-Klasse, die Ihnen MyInterface
-Objekt
Definieren Sie dann die Spring-Datei bean.xml wie folgt. Bitte beachten Sie, dass myinterface
bean als Prototyp definiert ist (es wird Ihnen also immer eine neue Instanz geben).
Definieren Sie anschließend die FactoryBean mit dem Namen der Factory-Methode.
%Vor% Jetzt können Sie myinterfaceFactory
aufrufen, um eine neue Instanz zu erhalten.
Behalten Sie die Spring-Konfigurationsdatei beans.xml im Stammverzeichnis des Klassenpfads. Durch die Angabe von scope = prototype werden für jeden getBean-Methodenaufruf verschiedene Instanzen von Bean ausgegeben.
beans.xml
%Vor%Wenn Sie möchten, dass Spring dieselbe Bean-Instanz jedes Mal zurückgibt, wenn eine benötigt wird, sollten Sie das Attribut scope des Beans als Singleton deklarieren.
Sobald der IoC-Container initialisiert ist, können Sie Ihre Spring-Beans abrufen. Aber vergewissere dich, dass du die folgende Initialisierung nur einmal durchführst.
%Vor%Dann können Sie Ihren Code wie folgt ändern.
%Vor% Angesichts des Kontextes, den Sie mir in Ihrem Kommentar gegeben haben, würde ich vorschlagen, dass Sie nicht die MyImplClass
-Exemplare haben, die von Spring erstellt wurden. Dieses Prototyp-Objekt, das von Spring instanziiert wurde, bietet keinen Nutzen von dem, was ich sagen kann.
Der beste Weg meiner Meinung nach, mit dem IoC-Muster hier zu bleiben, wäre statt dessen eine Spring-verwaltete Factory zu verwenden, die Instanzen von MyImplClass
erzeugt. Etwas nach dem Motto:
Je nach Verwendungsbedarf können Sie die Factory-Schnittstelle ändern, um MyImplClass
zurückzugeben, oder eine Logik hinzufügen, um eine andere Implementierung von MyInterface
zurückzugeben.
Ich denke eher, dass Fabriken und IoC / DI ziemlich gut zusammenarbeiten, und Ihr Anwendungsfall ist ein ziemlich gutes Beispiel dafür.
Anfangshinweis 1
Anstatt Threads manuell zu erstellen und zu starten, würde ich vorschlagen, einen Pool von Threads zu verwenden, der extern konfiguriert ist, sodass Sie die Anzahl der erstellten Threads verwalten können. Wenn die Größe von someList
1000 ist, ist die Erstellung so vieler Threads ineffizient. Sie sollten besser einen Executor verwenden, der von einem Thread-Pool unterstützt wird. Spring stellt einige Implementierungen bereit, die als Spring-Beans mit dem Namespace task
konfiguriert werden können, etwa so:
queue-capacity
ist die maximale Größe des Thread-Pools. Wenn diese Größe überschritten wird, führt der aktuelle Thread die zusätzliche Task aus und blockiert so die Schleife, bis ein anderer Thread freigegeben wird ( rejection-policy="CALLER_RUNS"
). Sehen Sie sich die task:executor
-Dokumentation an oder definieren Sie ThreadPoolExecutor
(spring oder jdk-concurrent) mit Ihrer eigenen Konfiguration.
Anfangshinweis 2
Wenn der einzige Staat, den Sie in MyClassImpl
speichern wollen, der Gegenstand aus der Liste ist, dann können Sie den Rest der folgenden Erklärung vergessen (mit Ausnahme der ThreadPool-Sachen) und direkt eine Singleton-Bohne verwenden: entfernen Sie die Runnable
interface und seine Methode no-arg run()
, füge eine run(OtherClass obj)
-Methode hinzu und mache so etwas:
Wenn Sie beabsichtigen, während der Ausführung von run()
(außer dem verarbeiteten Objekt) einen Zustand in MyClassImpl zu speichern, lesen Sie weiter. Sie verwenden jedoch weiterhin die Methode run(OtherClass obj)
anstelle von no-args run()
.
Die Grundidee besteht darin, für jeden laufenden Thread ein anderes Objekt zu erhalten, basierend auf einer Art von Modell oder Prototyp, die als Frühlingsbohne definiert ist. Um dies zu erreichen, definieren Sie einfach die Bean, die Sie zunächst an jeden Thread übergeben möchten, als Proxy, der an eine Instanz sendet, die an den laufenden Thread gebunden ist. Das bedeutet, dass in jeden Thread die gleiche Task-Instanz eingefügt wird, und während der Thread-Ausführung wird die reale Task, auf die Sie Methoden anwenden, an den aktuellen Thread gebunden.
Hauptprogramm
Da Sie die Elemente der Liste verwenden, um Ihr Geschäft zu erledigen, übergeben Sie jedes Element an seine eigene Aufgabe.
%Vor% Wir nehmen an, dass Program
eine Frühlingsbohne ist, daher können die Abhängigkeiten injiziert werden. Wenn Program
keine Spring-Bean ist, müssen Sie den Spring ApplicationContext von irgendwo und dann autowire Program
holen (d. H. Abhängigkeiten in den ApplicationContext basierend auf Annotationen einfügen). So etwas (im Konstruktor):
Definieren Sie die Aufgabe
%Vor% taskTarget
ist der Ort, an dem Sie Ihr Geschäft definieren. Diese Bean wird als Prototyp definiert, da jedem Thread eine neue Instanz zugewiesen wird. Dadurch können Sie sogar den Status speichern, der vom Parameter run()
abhängt. Diese Bean wird niemals direkt von der Anwendung verwendet (also autowire-candidate="false"
), aber sie wird über die task
-Bohne verwendet. In executeConcurrently()
oben wird die Zeile task.run(obj)
tatsächlich auf einen der vom Proxy erstellten Prototyp taskTarget
verteilt.
Wenn Sie zur Laufzeit feststellen können, welche MyImplClass
Instanz zu verwenden ist, könnten Sie alle Implementierungen als Beans in Ihrem Kontext-XML und @Autowire
einem Array vom Typ MyInterface
auflisten, um alle MyInterface
Implementoren zu erhalten.
Gegeben ist folgendes im Kontext xml:
%Vor%Dann eine Verzögerung
%Vor% führt zu allInterfaceBeans
, das beide oben definierten Beans enthält.
Wenn Sie möchten, dass die Logik bestimmt, welche Implementierung zur Injektionszeit verwendet werden soll, können Sie @Autowire
eine Setter-Methode setAllInterfaceBeans(MyInterface[] allInterfaceBeans);
.
In erster Linie wissen wir alle, dass Spring Container standardmäßig im Singleton-Modus erstellt werden (wenn Sie den Bereich nicht explizit angeben). Wie der Name schon sagt, garantiert singleton, dass Sie jedes Mal, wenn Sie die Bean aufrufen, dieselbe Instanz erhalten. Dennoch gibt es leichte Unterschiede zwischen Singleton im Frühjahr und dem von GoF erwähnten Singleton. Im Frühjahr wird die erstellte Instanz auf den Container beschränkt (nicht JVM wie wir in GoF gefunden haben).
Darüber hinaus können Sie im Frühjahr zwei verschiedene Bean-Instanzen desselben Typs mit unterschiedlichen Namen definieren, und es werden zwei verschiedene Instanzen auf dem Heap erstellt. Aber jedes Mal, wenn Sie einen dieser Beans nach Namen referenzieren (ref = in einer Bean-Definition oder getBean im appContext), erhalten Sie jedes Mal das gleiche Objekt. Das ist offensichtlich anders als das tatsächliche Singleton-Muster, aber ähnlich im Konzept sowieso.
Im Allgemeinen gibt es Auswirkungen der Verwendung eines Singleton in einer Multithread-Anwendung (Spring Singleton oder Singleton). Jeder Status, den Sie für diese Objekte beibehalten, muss berücksichtigen, dass mehrere Threads darauf zugreifen. Normalerweise wird jeder vorhandene Zustand während der Instanziierung über ein Setter- oder Konstruktorargument festgelegt. Diese Kategorie von Spring Bean ist sinnvoll für langlebige Objekte, thread-sichere Objekte. Wenn Sie möchten, dass etwas threadspezifisch ist und immer noch möchte, dass spring das Objekt erstellt, funktioniert der Prototypbereich.
Tags und Links java spring inversion-of-control