MyInterface
というインターフェースがあります。 MyInterface
を実装するクラス(これをMyImplClass
と呼びます)もRunnable
インターフェースを実装するため、これを使用してスレッドをインスタンス化できます。これが私のコードです。
for (OtherClass obj : someList) {
MyInterface myInter = new MyImplClass(obj);
Thread t = new Thread(myInter);
t.start();
}
私がやりたいことは、ApplicationContext.xmlで実装クラスを宣言し、反復ごとに新しいインスタンスを取得することです。したがって、私のコードは次のようになります。
for (OtherClass obj : someList) {
MyInterface myInter = // getting the implementation from elsewhere
Thread t = new Thread(myInter);
t.start();
}
可能であればIoCパターンを維持したい。
どうすればできますか?
ありがとう
以下のようなスプリングスコープのプロトタイプでファクトリーパターンを試すことができます。 MyInterface
オブジェクトを提供する抽象ファクトリクラスを定義します
public abstract class MyInterfaceFactoryImpl implements MyInterfaceFactory {
@Override
public abstract MyInterface getMyInterface();
}
次に、Spring bean.xmlファイルを以下のように定義します。 myinterface
Beanはプロトタイプとして定義されていることに注意してください(したがって、常に新しいインスタンスが提供されます)。
<bean name="myinterface" class="com.xxx.MyInterfaceImpl" scope="prototype"/>
次に、factorybeanをファクトリメソッド名で定義します。
<bean name="myinterfaceFactory" class="com.xxx.MyInterfaceFactoryImpl">
<lookup-method bean="myinterface" name="getMyInterface" />
</bean>
これで、myinterfaceFactory
を呼び出して新しいインスタンスを取得できます。
for (OtherClass obj : someList) {
MyInterface myInter = myInterfaceFactory.getMyInterface();
Thread t = new Thread(myInter);
t.start();
}
最初の注記1
手動でスレッドを作成して開始する代わりに、外部で構成されたスレッドのプールを使用して、作成されるスレッドの数を管理できるようにすることをお勧めします。 someList
のサイズが1000の場合、非常に多くのスレッドを作成することは非効率的です。スレッドのプールに裏打ちされたエグゼキューターを使用するほうがよいでしょう。 Springは、task
名前空間で構成されたSpring Beanとして使用できるいくつかの実装を提供します。
_<task:executor id="executor" queue-capacity="10" rejection-policy="CALLER_RUNS" />
_
_queue-capacity
_は、スレッドプールの最大サイズです。そのサイズを超えると、現在のスレッドが追加のタスクを実行し、別のスレッドが解放されるまでループをブロックします(_rejection-policy="CALLER_RUNS"
_)。 _task:executor
_のドキュメントを参照するか、独自の構成でThreadPoolExecutor
(springまたはjdk-concurrent)を定義してください。
最初の注記2
MyClassImpl
に格納する唯一の状態がリストの項目である場合、以下の説明の残りの部分(ThreadPoolを除く)を忘れて、シングルトンBeanを直接使用することができます。 Runnable
インターフェースとその引数なしのrun()
メソッド、run(OtherClass obj)
メソッドを追加して、次のようにします。
_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);
}
_
(処理されたオブジェクト以外の)run()
の実行中にMyClassImpl内にいくつかの状態を格納する予定の場合は、読み続けてください。ただし、引数なしのrun(OtherClass obj)
ではなくrun()
メソッドを使用します。
基本的な考え方は、Spring Beanとして定義されたある種のモデルまたはプロトタイプに基づいて、実行中のスレッドごとに異なるオブジェクトを取得することです。これを実現するには、各スレッドに最初に渡したいBeanを、実行中のスレッドにバインドされているインスタンスにディスパッチするプロキシとして定義するだけです。つまり、タスクの同じインスタンスが各スレッドに挿入され、スレッドの実行中に、メソッドを呼び出す実際のタスクが現在のスレッドにバインドされます。
メインプログラム
リストの要素を使用してビジネスを行うので、各要素を所有するタスクに渡します。
_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);
}
}
}
_
Program
はSpring Beanであると想定しているため、依存関係を注入できます。 Program
がSpring Beanでない場合は、どこかからSpring ApplicationContextを取得してから、Program
を自動配線する必要があります(つまり、注釈に基づいて、ApplicationContextで見つかった依存関係を挿入します)。このようなもの(コンストラクタで):
_public Program(ApplicationContext ctx) {
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
_
タスクを定義する
_<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
はビジネスを定義する場所です。このインスタンスは、新しいインスタンスが各スレッドに割り当てられるため、プロトタイプとして定義されます。これにより、run()
パラメータに依存する状態を保存することもできます。このBeanがアプリケーションで直接使用されることはありません(したがって_autowire-candidate="false"
_)。ただし、task
Beanを通じて使用されます。上記のexecuteConcurrently()
では、行task.run(obj)
は実際には、プロキシによって作成されたプロトタイプtaskTarget
の1つにディスパッチされます。
Spring構成ファイルbeans.xmlをクラスパスのルートに保持します。 scope = prototypeを作成すると、getBeanメソッドの呼び出しごとに異なるBeanインスタンスが生成されます。
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>
同様に、Springが必要になるたびに同じBeanインスタンスを返すようにする場合は、Beanのスコープ属性をシングルトンとして宣言する必要があります。
IoCコンテナが初期化されると、Spring Beanを取得できます。ただし、以下の初期化は1回だけ実行してください。
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
次に、以下のようにコードを変更できます。
for (OtherClass obj : someList) {
MyInterface myInter = (MyInterface ) context.getBean("myinterface");
Thread t = new Thread(myInter);
t.start();
}
コメントで提供されたコンテキストを踏まえて、Springによって作成されたMyImplClass
インスタンスを持たないことをお勧めします。このプロトタイプ化されたオブジェクトをSpringでインスタンス化しても、私が言えることから何の利益も得られません。
私の意見では、IoCパターンを維持するための最善の方法は、代わりにMyImplClass
のインスタンスを生成するSpringマネージドファクトリを利用することです。これに沿った何か:
public class MyInterfaceFactory {
public MyInterface newInstance(final OtherClass o) {
return new MyImplClass(o);
}
}
使用法のニーズに応じて、このファクトリのインターフェースを変更してMyImplClass
を返すか、MyInterface
の異なる実装を返すロジックを追加できます。
私は、ファクトリーとIoC/DIはかなりうまく連携していると思う傾向があり、あなたのユースケースはその良い例です。
使用するMyImplClass
インスタンスを実行時に決定できる場合、すべての実装をBeanとしてコンテキストxmlにリストし、_@Autowire
_タイプMyInterface
の配列をリストして、すべてのMyInterface
実装者。
コンテキストxmlで次の場合:
_<bean class="MyImplClass" p:somethingCaseSpecific="case1"/>
<bean class="MyImplClass" p:somethingCaseSpecific="case2"/>
_
その後、減速
_@Autowire
MyInterface[] allInterfaceBeans;
_
上記で定義された両方のBeanを含むallInterfaceBeans
になります。
インジェクション時にどの実装を使用するかを決定するロジックが必要な場合は、常に_@Autowire
_セッターメソッドsetAllInterfaceBeans(MyInterface[] allInterfaceBeans);
を使用できます。
何よりもまず、デフォルトでは、SpringコンテナがBeanをシングルトンモードで作成することを知っています(スコープを明示的に指定しない場合)。名前が示すように、シングルトンはBeanを呼び出すたびに同じインスタンスを提供することを保証します。それでも、春のシングルトンとGoFで言及されているシングルトンとの間にはわずかな違いがあります。 Springでは、作成されたインスタンスはコンテナーに制限されます(GoFで見つかったJVMではありません)。
さらに、春には、同じタイプで名前が異なる2つの異なるBeanインスタンスを定義でき、それらはヒープ上に作成される2つの異なるインスタンスになります。ただし、これらのBeanの1つを名前で参照するたびに(Bean定義のref =またはappContextのgetBean)、毎回同じオブジェクトを取得します。これは実際のシングルトンパターンとは明らかに異なりますが、概念は同じです。
一般的に、マルチスレッドアプリケーションでシングルトンを使用することには影響があります(スプリングシングルトンまたは実際のシングルトン)。これらのオブジェクトで保持するすべての状態は、複数のスレッドがそれにアクセスするという事実を考慮する必要があります。通常、存在する状態は、インスタンス化中にセッターまたはコンストラクター引数を介して設定されます。このSpring Beanのカテゴリは、長期間有効なオブジェクト、つまりスレッドセーフオブジェクトに適しています。スレッド固有の何かが必要で、オブジェクトを作成するためにSpringが必要な場合は、プロトタイプスコープが機能します。