web-dev-qa-db-de.com

Holen Sie sich eine neue Instanz einer Frühlingsbohne

Ich habe eine Schnittstelle namens MyInterface. Die Klasse, die MyInterface implementiert (nennen wir sie MyImplClass), implementiert auch die Runnable-Schnittstelle, sodass ich damit Threads instanziieren kann. Dies ist jetzt mein Code.

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

Ich möchte die implementierende Klasse in meiner ApplicationContext.xml deklarieren und für jede Iteration eine neue Instanz erhalten. Mein Code sieht also 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 das IoC-Muster nach Möglichkeit beibehalten. 
Wie kann ich das tun? 
Vielen Dank

8
Mr T.

Sie können ein Fabrikmuster mit einem Federprüfmuster wie unten gezeigt ausprobieren. Definieren Sie eine Abstract Factory-Klasse, die Ihnen ein MyInterface-Objekt gibt

public abstract class MyInterfaceFactoryImpl implements MyInterfaceFactory {

@Override
public abstract MyInterface getMyInterface();

}

Definieren Sie anschließend die Datei Spring bean.xml wie folgt. Bitte beachten Sie, dass myinterface bean als Prototyp definiert ist (Sie erhalten also immer eine neue Instanz).

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

Definieren Sie dann die Factorybean mit dem Namen der Factory-Methode.

<bean name="myinterfaceFactory" class="com.xxx.MyInterfaceFactoryImpl">
    <lookup-method bean="myinterface" name="getMyInterface" />
</bean>

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

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

Ursprüngliche Anmerkung 1

Anstatt Threads von Hand 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 das Erstellen so vieler Threads ineffizient. Sie sollten besser einen Executor verwenden, der durch einen Pool von Threads gesichert wird. Spring stellt einige Implementierungen bereit, die als Spring-Beans verwendet werden können, die mit dem Namespace task konfiguriert sind, etwa wie folgt:

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

queue-capacity ist die maximale Größe des Threads-Pools. Wenn diese Größe überschritten wird, führt der aktuelle Thread die zusätzliche Task aus und blockiert die Schleife, bis ein anderer Thread freigegeben wird (rejection-policy="CALLER_RUNS"). Sehen Sie sich die task:executor-Dokumentation an oder definieren Sie eine ThreadPoolExecutor (spring oder jdk-concurrent) mit Ihrer eigenen Konfiguration.

Ursprüngliche Anmerkung 2

Wenn der einzige Status, den Sie in MyClassImpl speichern möchten, das Element aus der Liste ist, können Sie den Rest der folgenden Erklärung (außer den ThreadPool-Kram) vergessen und direkt eine Singleton-Bean verwenden: Entfernen Sie die Runnable-Schnittstelle und deren Nr -arg run()-Methode, fügen Sie eine run(OtherClass obj)-Methode hinzu und führen Sie Folgendes 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 während der Ausführung von run() (außer dem verarbeiteten Objekt) einen Status in MyClassImpl speichern möchten, 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, das auf einem Modell oder Prototyp basiert, der als Spring-Bean definiert ist. Um dies zu erreichen, definieren Sie einfach die Bean, die Sie anfänglich an jeden Thread übergeben möchten, als einen Proxy, der an eine Instanz gesendet wird, die an den laufenden Thread gebunden ist. Dies bedeutet, dass dieselbe Task-Instanz in jeden Thread eingefügt wird. Während der Thread-Ausführung wird die reale Task, für die Sie Methoden aufrufen, an den aktuellen Thread gebunden.

Hauptprogramm

Da Sie die Elemente der Liste für Ihre Geschäfte verwenden, übergeben Sie jedes Element an seine eigentliche Aufgabe.

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 nehmen an, dass Program eine Spring-Bean ist, daher können die Abhängigkeiten eingespritzt werden. Wenn Program kein Spring-Bean ist, müssen Sie den Spring ApplicationContext von irgendwo abrufen und autowire Program verwenden (d. H. Abhängigkeiten einfügen, die im ApplicationContext gefunden werden, basierend auf Anmerkungen). So etwas (im Konstruktor):

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

Definiere 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>

In taskTarget definieren Sie Ihr Geschäft. Diese Bean ist als Prototyp definiert, da jedem Thread eine neue Instanz zugewiesen wird. Dank dessen können Sie sogar Zustände speichern, die vom Parameter run() abhängen. Diese Bean wird nie direkt von der Anwendung verwendet (also autowire-candidate="false"), aber sie wird durch die task-Bean verwendet. In executeConcurrently() wird die Zeile task.run(obj) tatsächlich auf einem Prototyp taskTarget abgesetzt, der vom Proxy erstellt wurde.

2
Gaetan

Lassen Sie die Federkonfigurationsdatei "beans.xml" im Stammverzeichnis des Klassenpfads . Wenn Sie scope = prototype erstellen, werden für jeden Aufruf der getBean-Methode unterschiedliche Bean-Instanzen ausgeführt.

bohnen.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>

Wenn Sie möchten, dass Spring jedes Mal, wenn eine Instanz benötigt wird, dieselbe Bean-Instanz zurückgibt, sollten Sie das Bereichsattribut der Bean als Singleton deklarieren.

Sobald der IoC-Container initialisiert ist, können Sie Ihre Spring-Beans abrufen. Stellen Sie jedoch sicher, dass Sie die unten aufgeführten Initialisierungen nur einmal durchführen.

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();
}
2
Albin

Angesichts des Kontextes, den Sie mir in Ihrem Kommentar zur Verfügung gestellt haben, würde ich vorschlagen, dass Sie nicht die von Spring erstellten MyImplClass-Instanzen haben. Dieses von Spring instanziierte Objekt bringt keinen Vorteil von dem, was ich sagen kann.

Der beste Weg, um mich an das IoC-Muster zu halten, wäre meiner Meinung nach, stattdessen eine von Spring verwaltete Factory zu verwenden, die Instanzen von MyImplClass erzeugt. Etwas in der Richtung davon:

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

Abhängig von den Verwendungsanforderungen können Sie die Schnittstelle dieser Factory ändern, um MyImplClass zurückzugeben, oder eine Logik hinzufügen, um eine andere Implementierung von MyInterface zurückzugeben.

Ich neige dazu zu glauben, dass Factories und IoC/DI ziemlich gut zusammenarbeiten, und Ihr Anwendungsfall ist ein ziemlich gutes Beispiel dafür.

2

Wenn Sie zur Laufzeit bestimmen können, welche MyImplClass-Instanz verwendet werden soll, können Sie alle Implementierungen als Beans in Ihrem Kontext-XML und @Autowire ein Array des Typs MyInterface auflisten, um alle MyInterface-Implementierer abzurufen.

Im Kontext von xml gilt Folgendes:

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

Dann eine Verzögerung

@Autowire
MyInterface[] allInterfaceBeans;

führt dazu, dass allInterfaceBeans beide oben definierten Beans enthält.

Wenn Sie die Logik zum Bestimmen der zu verwendenden Implementierung zur Einspritzzeit wünschen, können Sie @Autowire immer eine Setter-Methode setAllInterfaceBeans(MyInterface[] allInterfaceBeans); verwenden.

0
Ben Kern

In erster Linie wissen wir alle, dass der Spring-Container standardmäßig ein Bean im Singleton-Modus erstellt (wenn Sie den Bereich nicht explizit angeben). Wie der Name schon sagt, garantiert Singleton, dass Sie bei jedem Aufruf der Bean dieselbe Instanz erhalten. Trotzdem gibt es leichte Unterschiede zwischen Singleton im Frühjahr und dem von GoF erwähnten. Im Frühjahr wird die erstellte Instanz auf den Container beschränkt (nicht die JVM, wie wir sie in GoF gefunden haben). 

Außerdem können Sie im Frühjahr zwei verschiedene Bean-Instanzen desselben Typs mit unterschiedlichen Namen definieren. Dabei handelt es sich um zwei verschiedene Instanzen, die auf dem Heap erstellt werden. Jedes Mal, wenn Sie auf eine dieser Beans mit Namen verweisen (ref = in einer Bean-Definition oder getBean im appContext), erhalten Sie jedes Mal dasselbe Objekt. Das ist offensichtlich anders als das eigentliche Singleton-Pattern, aber im Konzept ist es ohnehin ähnlich.

Im Allgemeinen hat die Verwendung eines Singleton in einer Multithread-Anwendung (Spring-Singleton oder tatsächliches Singleton) Auswirkungen. Jeder Zustand, den Sie für diese Objekte beibehalten, muss der Tatsache Rechnung tragen, dass mehrere Threads darauf zugreifen. Normalerweise wird jeder vorhandene Status während der Instantiierung über ein Setter- oder Konstruktorargument festgelegt. Diese Kategorie von Springbohnen ist für langlebige Objekte und fadensichere Objekte sinnvoll. Wenn Sie etwas Thread-spezifisches wollen und dennoch das Objekt von Spring erstellen möchten, funktioniert der Prototypbereich.

0
MCHAppy