2015-04-07 8 views
8

Ich habe eine Schnittstelle namens MyInterface. Die Klasse MyInterface (wir können sie MyImplClass nennen) implementiert auch die Schnittstelle Runnable, sodass ich sie zum Instanziieren von Threads verwenden kann. Das ist jetzt mein Code.Holen Sie sich eine neue Instanz einer Frühlingsbohne

for (OtherClass obj : someList) { 
    MyInterface myInter = new MyImplClass(obj); 
    Thread t = new Thread(myInter); 
    t.start(); 
} 

Was ich tun möchte, ist die implementierende Klasse in meinem applicationContext.xml zu erklären und eine neue Instanz für jede Iteration erhalten. So sieht mein Code ungefähr so ​​aus:

for (OtherClass obj : someList) { 
    MyInterface myInter = // getting the implementation from elsewhere 
    Thread t = new Thread(myInter); 
    t.start(); 
} 

Ich möchte immer noch das IoC-Muster beibehalten, wenn möglich.
Wie kann ich das tun?
Dank

+0

haben Sie das versucht? 'MyClass myClass = applicationContext.getBean (" myClass ");' –

+0

werfen Sie auch einen Blick auf die Antwort dieser Frage. http://stackoverflow.com/questions/812415/why-is-springs-applicationcontext-getbean-considered-bad –

+0

Wie ich verstehe - 'ApplicationContext.getBean' ist nicht IoC. Außerdem schlägt der von Ihnen vorgeschlagene Link eine Einzelinstanzlösung vor. dafür kann ich Inject annotaion verwenden - das ist allgemeiner (nicht federabhängig). –

Antwort

12

Sie können Fabrik Muster mit Frühling Umfang Prototyp wie unten versuchen. Definieren Sie eine Abstract Factory-Klasse, die Sie MyInterface Objekt

public abstract class MyInterfaceFactoryImpl implements MyInterfaceFactory { 

@Override 
public abstract MyInterface getMyInterface(); 

} 

dann wie unter der Frühling bean.xml Datei definieren. Bitte beachten Sie, myinterface Bean ist als Prototyp definiert (so wird es Ihnen immer neue Instanz geben).

<bean name="myinterface" class="com.xxx.MyInterfaceImpl" scope="prototype"/> 

Definieren Sie anschließend die FactoryBean mit dem Namen der Factory-Methode.

Jetzt können Sie myinterfaceFactory anrufen, um neue Instanz zu erhalten.

for (OtherClass obj : someList) { 
     MyInterface myInter = myInterfaceFactory.getMyInterface(); 
     Thread t = new Thread(myInter); 
     t.start(); 
} 
+0

Verwaltet, damit es funktioniert! Vielen Dank :) –

0

Wenn Sie zur Laufzeit die MyImplClass Instanz verwenden, bestimmen können, können Sie alle Implementierungen wie Bohnen in Ihrem Kontext xml und @Autowire ein Array vom Typ MyInterface zu bekommen alle MyInterface Implementierer Liste könnte.

die folgenden im Rahmen xml Gegeben:

<bean class="MyImplClass" p:somethingCaseSpecific="case1"/> 
<bean class="MyImplClass" p:somethingCaseSpecific="case2"/> 

Dann wird eine Verzögerung

@Autowire 
MyInterface[] allInterfaceBeans; 

in allInterfaceBeans führen beide Bohnen oben definiert enthält.

Wenn Sie möchten, dass die Logik zur Bestimmung der zu verwendenden Implementierung zur Injektionszeit verwendet wird, können Sie immer @Autowire eine Setter-Methode setAllInterfaceBeans(MyInterface[] allInterfaceBeans);.

+0

Danke, aber was ich von Ihrer Lösung verstehe, ist, dass ich eine einzelne Instanz jeder implementierenden Klasse bekomme, wo ich mehrere Instanzen der implementierenden Klasse brauche (und mir egal, welche Klasse das ist) –

2

Halten Sie die Spring-Konfigurationsdatei beans.xml im Stammverzeichnis des Klassenpfads. Durch die Angabe von scope = prototype werden für jeden getBean-Methodenaufruf unterschiedliche Instanzen von Bean ausgegeben.

beans.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation=" 
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans.xsd"> 

<bean id="myinterface" class="MyImplClass" scope="prototype"/> 
</beans> 

ähnlich wie wenn Sie wollen Frühling die gleiche Bean-Instanz jedes Mal, wenn einer benötigt wird, um zurückzukehren, sollten Sie den Anwendungsbereich der Bohne erklären Attribut Singleton zu sein.

Sobald der IoC-Container initialisiert ist, können Sie Ihre Spring-Beans abrufen. Aber vergewissere dich, dass du die unten angegebene Initialisierung nur einmal machst.

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 

Dann können Sie Ihren Code wie folgt ändern.

for (OtherClass obj : someList) { 
MyInterface myInter = (MyInterface) context.getBean("myinterface"); 
Thread t = new Thread(myInter); 
t.start(); 
} 
+0

Danke, aber diese Lösung ist Frühling abhängig davon, dass der Einspritzmechanismus eine Feder sein muss. Das allein macht es nicht IoC. Worst-Case-Szenario, in der Tat werde ich diese Lösung verwenden, aber ich fragte mich, wie man innerhalb der IoC-Muster –

+0

Lösung von @Himorithm wird gut funktionieren. Go thorugh http://docs.spring.io/spring/docs/4.1.6.RELEASE/spring-framework-reference/html/beans.html#beans-factory-method-injection – Albin

0

In erster Linie wissen wir alle, dass Federbehälter durch Standard Bean in Singleton-Modus erstellen (wenn Sie nicht explizit den Umfang 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 im Konzept sowieso ähnlich.

Im Allgemeinen gibt es Auswirkungen der Verwendung eines Singleton in einer Multi-Thread-Anwendung (Spring Singleton oder tatsächlichen 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 etwas threadspezifisches wollen und immer noch wünschen, dass spring das Objekt erstellt, dann funktioniert der Prototypbereich.

2

Angesichts des Kontextes, den Sie mir in Ihrem Kommentar gegeben haben, würde ich vorschlagen, dass Sie nicht die MyImplClass Instanzen von Spring erstellt haben. 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 halten wäre stattdessen eine Spring-verwaltete Factory verwenden, die Instanzen von MyImplClass erzeugt. Etwas nach dem Vorbild dieses:

public class MyInterfaceFactory { 
    public MyInterface newInstance(final OtherClass o) { 
     return new MyImplClass(o); 
    } 
} 

auf den Nutzungsbedarf Je, können Sie diese Fabrik Schnittstelle ändern MyImplClass zurückzukehren, oder eine gewisse Logik hinzufügen, eine andere Implementierung von MyInterface zurückzukehren.

Ich denke, dass Fabriken und IoC/DI ziemlich gut zusammenarbeiten, und Ihr Anwendungsfall ist ein ziemlich gutes Beispiel dafür.

1

Initial Anmerkung 1

Statt zu schaffen und Fäden von Hand starten, würde ich vorschlagen, einen Pool von Threads zu verwenden, die extern konfiguriert ist, so dass Sie die Anzahl der Threads verwalten, die erstellt. Wenn die Größe someList 1000 ist, ist die Erstellung so vieler Threads ineffizient. Sie sollten besser einen Executor verwenden, der von einem Thread-Pool unterstützt wird. Frühling bietet einige Implementierungen, die als Feder Bohnen mit dem task Namespace, in etwa so konfiguriert werden, verwendet werden, können:

<task:executor id="executor" queue-capacity="10" rejection-policy="CALLER_RUNS" /> 

queue-capacity die maximale Größe des Threads Pool. 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"). Siehe die task:executor Dokumentation, oder definieren Sie ThreadPoolExecutor (Spring oder jdk-concurrent) mit Ihrer eigenen Konfiguration.

Initial Anmerkung 2

Wenn der einzige Staat, der Sie in MyClassImpl ist das Element aus der Liste speichern wollen, dann können Sie den Rest der Erklärung unten vergessen (mit Ausnahme des Thread Sachen) und verwenden, um direkt eine Singleton-Bohne: entfernen Sie die Runnable Schnittstelle und deren nicht argument run() Methode, fügen Sie ein run(OtherClass obj) Verfahren und etwas tun, wie folgt aus:

final MyInterface task = // get it from spring as a singleton 
for (final OtherClass obj : someList) { 
    executor.execute(new Runnable() { 
    public void run() {task.run(obj);} 
    }); 
    // jdk 8 : executor.execute(task::run); 
} 

Wenn Sie einige Zustand innerhalb MyClassImpl während der executi speichern möchten von run() (außer dem verarbeiteten Objekt), lesen Sie weiter. Aber Sie werden immer noch die run(OtherClass obj) Methode anstelle von No-Args run() verwenden.

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. Dies bedeutet, dass in jeden Thread die gleiche Task-Instanz eingefügt wird. Während der Thread-Ausführung ist die reale Task, auf die Sie Methoden anwenden, an den aktuellen Thread gebunden.

Hauptprogramm

Da Sie die Elemente der Liste sind mit Ihrem Unternehmen zu tun, werden Sie jedes Element auf seine Aufgabe zu besitzen passieren.

public class Program { 
    @Resource private MyInterface task; // this is a proxy 
    @Resource private TaskExecutor executor; 

    public void executeConcurrently(List<OtherClass> someList) { 
    for (final OtherClass obj : someList) { 
     executor.execute(new Runnable() { 
     public void run() { task.run(obj); } 
     }); 
     // jdk 8 : executor.execute(task::run); 
    } 
    } 
} 

Wir vermuten, dass Program eine Feder Bohne, damit die Abhängigkeiten injiziert werden kann. Wenn Program keine Spring Bean ist, müssen Sie den Spring-Anwendungskontext von irgendwo herholen, dann autowire Program (d. H. Abhängigkeiten in den ApplicationContext basierend auf Anmerkungen einfügen). So etwas wie dies (im Konstruktor):

public Program(ApplicationContext ctx) { 
    ctx.getAutowireCapableBeanFactory().autowireBean(this); 
} 

definieren die Aufgabe

<bean id="taskTarget" class="MyImplClass" scope="prototype" autowire-candidate="false" /> 

<bean id="task" class="org.springframework.aop.framework.ProxyFactoryBean"> 
    <property name="targetSource"> 
    <bean class="org.springframework.aop.target.ThreadLocalTargetSource"> 
     <property name="targetBeanName" value="taskTarget"/> 
     <property name="targetClass" value="MyInterface"/> 
    </bean> 
    </property> 
</bean> 

taskTarget, in dem Sie Ihr Unternehmen definieren. Diese Bean wird als Prototyp definiert, da jedem Thread eine neue Instanz zugewiesen wird. Dadurch können Sie sogar den Zustand speichern, der vom run() Parameter abhängt. Diese Bohne wird niemals direkt von der Anwendung verwendet (also autowire-candidate="false"), aber sie wird durch die task Bean verwendet. In executeConcurrently() oben wird die Linie task.run(obj) tatsächlich auf einem der Prototyp taskTarget, die von dem Proxy erstellt wurde, ausgelöst werden.