16

我有一个在 tomcat 中运行的 Web 应用程序,我使用 ThreadPool (Java 5 ExecutorService) 并行运行 IO 密集型操作以提高性能。我想让每个池线程中使用的一些 bean 在请求范围内,但是 ThreadPool 中的线程无权访问 spring 上下文并获得代理失败。关于如何使 ThreadPool 中的线程可以使用 spring 上下文以解决代理故障的任何想法?

我猜必须有一种方法可以为每个任务在 ThreadPool 中注册/注销每个线程,但是没有任何运气找到如何做到这一点。

谢谢!

4

4 回答 4

48

我正在为需要访问请求范围的任务使用以下超类。基本上你可以扩展它并在 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();
}
于 2009-12-03T01:26:32.977 回答
12

I also wish I had 1000 votes to give to the currently accepted answer. I had been stumped on how to do this for some time. Based on it, here is my solution using the Callable interface in case you want to use some of the new @Async stuff in Spring 3.0.

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;
}
于 2010-12-02T00:50:23.517 回答
0

Spring 有一个ThreadPoolTask​​Executor类,您可以使用它来管理来自 Spring 的线程池。但是,看起来您必须做一些工作才能使 Spring 上下文可用于每个线程。

我不确定即使您以这种方式连接它是否会起作用。Spring 在线程本地使用令牌来定位请求(或会话)范围内的对象,因此如果您尝试从不同的线程访问请求范围 bean,则该令牌很可能不存在。

于 2009-10-10T13:34:08.093 回答
0

你能反过来试试吗?使用存储在请求范围内的数据容器并将其提供给线程池(也许将其放入队列中,以便线程池一次可以获取一个数据容器,对其进行处理,将其标记为“完成”并继续与下一个)。

于 2009-10-07T06:55:37.360 回答