2009-10-06 102 views
15

線程內的範圍代理bean我有一個運行在tomcat中的web應用程序,我使用ThreadPool(Java 5 ExecutorService)並行運行IO密集型操作來提高性能。我想在每個池中使用一些bean在請求範圍內,但ThreadPool中的線程不能訪問spring上下文並獲得代理失敗。關於如何使ThreadPool中的線程可用於解決代理失敗的任何想法?訪問線程

我在猜測必須有一種方法來註冊/註銷ThreadPool中的每個線程,每個任務都帶有彈簧,但是還沒有找到如何執行此操作的運氣。

謝謝!

回答

45

我對需要訪問請求範圍的任務使用以下超類。基本上你可以擴展它並在onRun()方法中實現你的邏輯。

import org.springframework.web.context.request.RequestAttributes; 
import org.springframework.web.context.request.RequestContextHolder; 

/** 
* @author Eugene Kuleshov 
*/ 
public abstract class RequestAwareRunnable implements Runnable { 
    private final RequestAttributes requestAttributes; 
    private Thread thread; 

    public RequestAwareRunnable() { 
    this.requestAttributes = RequestContextHolder.getRequestAttributes(); 
    this.thread = Thread.currentThread(); 
    } 

    public void run() { 
    try { 
     RequestContextHolder.setRequestAttributes(requestAttributes); 
     onRun(); 
    } finally { 
     if (Thread.currentThread() != thread) { 
     RequestContextHolder.resetRequestAttributes(); 
     } 
     thread = null; 
    } 
    } 

    protected abstract void onRun(); 
} 
+0

謝謝你的幫助尤金。非常感激! – Perulish8 2009-12-04 04:24:13

+3

哇。我希望我能像其他10個選票一樣把這件事弄糟。謝謝您的幫助。 – Cameron 2010-04-22 22:43:51

+2

似乎這種方法有死鎖,請參閱http://stackoverflow.com/q/15768556/438742 – kan 2013-04-02 15:52:54

0

你可以試試它嗎?使用存儲在請求範圍中的數據容器並將其提供給線程池(可能將其放入隊列中,以便線程池一次只能取一個數據容器,對其進行處理,將其標記爲「已完成」並繼續與下一個)。

0

Spring有一個ThreadPoolTaskExecutor類,您可以使用它來從Spring管理線程池。但是,看起來你需要做一些工作來使每個線程都可以使用Spring上下文。

即使您以這種方式進行連線,我也不確定它是否能正常工作。 Spring在局部線程中使用一個標記來定位請求(或會話)作用域中的對象,所以如果你試圖從一個不同的線程訪問一個請求作用域bean,很可能這個標記不會在那裏。

+0

這只是創建執行者的一種方式。這裏真正的問題是''但ThreadPool中的線程無法訪問spring上下文,並導致代理失敗。「 – msangel 2013-07-03 23:53:26

11

我也希望我有1000張選票給予目前接受的答案。一段時間以來,我一直被困在如何做到這一點。基於它,這裏是我使用Callable接口的解決方案,以防在Spring 3.0中使用一些新的@Async。

public abstract class RequestContextAwareCallable<V> implements Callable<V> { 

    private final RequestAttributes requestAttributes; 
    private Thread thread; 

    public RequestContextAwareCallable() { 
     this.requestAttributes = RequestContextHolder.getRequestAttributes(); 
     this.thread = Thread.currentThread(); 
    } 

    public V call() throws Exception { 
     try { 
      RequestContextHolder.setRequestAttributes(requestAttributes); 
      return onCall(); 
     } finally { 
      if (Thread.currentThread() != thread) { 
       RequestContextHolder.resetRequestAttributes(); 
      } 
      thread = null; 
     } 
    } 

    public abstract V onCall() throws Exception; 
} 
+0

gr8 !!我正在尋找這個:) – hop 2012-09-04 03:36:43

+0

好的,我有'@ Async'註釋的方法。這是returnig'未來'結果。我希望在代理生成的類裏面有'ThreadExecutor'和'Callabbe',但是如何強制它使用這種Callable? 作爲一種臨時解決方案,我只是將應用上下文引用傳遞給方法,它適用於我,但它看起來不太好。 – msangel 2013-07-03 23:58:42

+0

我認爲這段代碼可能有問題。 RequestContextHolder.getRequestAttributes()將返回當前請求中使用的相同實例,而不是副本。因此,您可能正在訪問servlet請求範圍之外的ServletRequestAttributes對象... – 2013-12-20 11:38:20