2012-09-27 8 views
8

Webアプリケーションでは、バックグラウンドサービスがあります。このサービスは、Engineクラスと複数スレッドを使用するように構成されたExecutorServiceを含むGeneratorクラスを使用し、GeneratorTasksを受け入れます。Spring BeanをExecutorServiceのThreadLocalインスタンスのように動作させる

@Component 
public class Generator { 
    @Autowired 
    private Engine heavyEngine; 

    private ExecutorService exec = Executors.newFixedThreadPool(3); 

    //I actually pass the singleton instance Generator class into the task. 
    public void submitTask(TaskModel model, TaskCallback callback) { 
     this.exec.submit(new GeneratorTask(model, this, callback)); 
    } 
} 

@Component 
public class Engine { 
    public Engine() { 
     //time-consuming initialization code here 
    } 
} 

public class GeneratorTask implements Callable<String> { 
    public GeneratorTask(TaskModel m, Generator g, ReceiptCallback c) { 
     this.m = m; 
     this.generator = g; 
     this.c = c; 
    } 

    public String call() throws Exception { 
     //This actually calls the Engine class of the generator. 
     //Maybe I should have passed the Engine itself? 
     this.generator.runEngine(c); 
    } 
} 

Engineクラスの初期化に時間がかかりますので、スレッドごとに1回のみ初期化したいのが理想です。インスタンスを複数のスレッドに渡って共有することはできないため、インスタンスをシングルトンインスタンスにすることはできません(シーケンシャル処理に依存します)。処理タスクが完了した後で、インスタンスを再利用することはまったく問題ありません。

私はprivate Engine heavyEngineを変数ThreadLocal変数にすることを考えていました。しかし、私はSpringにも新しいので、Spring注釈を使用してThreadLocal変数を挿入する別の方法があるかどうか疑問に思っていました。私はrequestスコープにBeanのスコープを見てきましたが、私はどのように私はそれが私のデザインを与えて行く必要がありますか分からない。

私のデザインを改善する方法についてのガイダンスは高く評価されます。

+0

:スレッドスコープのBeanを宣言するために、その後

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope" /> </entry> </map> </property> </bean> 

そして:ために

は、カスタムスコープを登録する必要があり、それを使用するにはBeanとして宣言しました。デフォルトでは、Spring Beanはシングルトンなので、 'Engine'が既にシングルトンであるという良いチャンスがあります... –

+0

申し訳ありませんが私は不明確かもしれません。私の問題は、処理ロジックのために複数の同時スレッドがアクセスできないため、エンジンをシングルトンにすることができないことです。完全な「仕事」が完了したら、エンジンを再利用することは安全です。そのため、私はそれを使用するスレッドごとにインスタンスを作成することを考えていました。 –

+0

私はあなたの現在のコードエンジンでは、コードが実際に何をしているのか望んでいなくても、すでにシングルトンであると言っていました。 –

答えて

9

まず第一にすべて放棄ThreadLocal - そのクラスには何か怖いものがあります。あなたが必要とするのは、単にオブジェクトのプールです。これはよく知られている機能はありませんが、春にも、これをサポートしています。あなたはengineを注入する際

<bean id="engineProto" class="Engine" scope="prototype" lazy-init="true"/> 

<bean id="engine" class="org.springframework.aop.framework.ProxyFactoryBean"> 
    <property name="targetSource"> 
     <bean class="org.springframework.aop.target.CommonsPoolTargetSource"> 
      <property name="targetClass" value="Engine"/> 
      <property name="targetBeanName" value="engineProto"/> 
      <property name="maxSize" value="3"/> 
      <property name="maxWait" value="5000"/> 
     </bean> 
    </property> 
</bean> 

は今、あなたが実際にプロキシオブジェクトを受け取ります(Engineはインターフェースが必要になります)に自由にオブジェクトへのすべての呼び出しを委任することプール。プールサイズは設定可能です。もちろんThreadLocalTargetSourceを使用するのを妨げるものは何もありませんが、Commons Poolの代わりにThreadLocalを使用しています。どちらの方法も、Engineへの排他的なスレッドセーフなアクセスを保証します。

最後に、手動でプーリングを使用できます(ただし、上記のソリューションの美しさは完全に透過的です)。定義に基づいてプールされるEJBに切り替えます。

+1

クラスローダーのメモリリークはちょっと怖いです。 – Vedran

+0

Engineインターフェースインスタンスが必要な場所に "engine" beanを挿入できますか?これは、舞台裏でプールを使用するEngineインターフェイスを継承する匿名クラスを作成するだけですか? – guitar80

1

Engineのファクトリを作成し、それをGeneratorTaskと呼びます。この方法で、Generator内のheavyEngineフィールドとGeneratorTaskGeneratorコンストラクタ引数を削除できます。
その後、初期化時間をEngineに保存する場合は、それをシングルトンとして宣言できますが、非スレッドセーフメソッドでは​​キーワードを使用します。

public class Generator {  
    @Autowired private EngineFactory engineFactory; 
    private ExecutorService exec = Executors.newFixedThreadPool(3); 

    public void submitTask(TaskModel model, TaskCallback callback) { 
     this.exec.submit(new GeneratorTask(engineFactory, model, callback)); 
    } 
} 

public class EngineFactory { 
    @Autowired private Engine instance; 

    public Engine getInstance() { 
     return instance; 
    } 
} 

public class Engine { 
    public Engine() { 
     //time-consuming initialization code here 
    } 

    public synchronized void runEngine() { 
     // Do non thread safe stuf 
    } 
} 

public class GeneratorTask implements Callable<String> { 
    public GeneratorTask(EngineFactory f, TaskModel m, ReceiptCallback c) { 
     this.f = f; 
     this.m = m; 
     this.c = c; 
    } 

    public String call() throws Exception { 
     Engine engine = f.getInstance(); 
     engine.runEngine(); 
     ... 
    } 
} 

おそらくエンジンをCallableに渡すための純粋なSpringの方法がありますが、この場合、工場は私の意見では十分です。

5

FYI、Spring 3.0以降には、スレッド対応のスコープ実装SimpleThreadScopeが含まれています。 `Engine`がautowiredされているので、私はあなたが持っていると仮定し

<bean id="myBean" class="com.foo.MyBean" scope="thread"> 
    ... 
</bean> 
関連する問題