2009-10-06 69 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