Доступ к расширенному прокси-серверу beans в потоках

У меня есть веб-приложение, работающее в tomcat, где я использую ThreadPool (Java 5 ExecutorService), чтобы параллельно выполнять интенсивные операции ввода-вывода для повышения производительности. Я хотел бы, чтобы некоторые из beans, используемые в каждом объединенном потоке, были в области запроса, но нити в ThreadPool не имеют доступа к контексту spring и получают прокси-сбой. Любые идеи о том, как сделать контекст spring доступным для потоков ThreadPool для устранения сбоев прокси-сервера?

Я предполагаю, что должен быть способ зарегистрировать/отменить регистрацию каждого потока в ThreadPool с помощью spring для каждой задачи, но не успел найти, как это сделать.

Спасибо!

Ответы

Ответ 1

Я использую следующий суперкласс для своих задач, которые должны иметь доступ к области запроса. В основном вы можете просто расширить его и реализовать свою логику в методе 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();
}

Ответ 2

Мне также жаль, что у меня не было 1000 голосов, чтобы ответить на принятый в настоящее время ответ. Я был в тупике, как это сделать в течение некоторого времени. Основываясь на этом, вот мое решение с использованием интерфейса Callable, если вы хотите использовать некоторые из новых вещей @Async в 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;
}

Ответ 3

Не могли бы вы попробовать по-другому? Используйте контейнер данных, который хранится в области запроса, и передайте его пулу потоков (возможно, поместите его в очередь, чтобы пул потоков мог одновременно принимать один контейнер данных, работать с ним, отмечать его как "сделанный" и продолжать со следующим).

Ответ 4

Spring имеет класс ThreadPoolTaskExecutor, который можно использовать для управления пулом потоков из Spring. Тем не менее, похоже, что вам нужно будет сделать некоторую работу, чтобы сделать контекст Spring доступным для каждого потока.

Я не уверен, что это сработает, даже если вы все-таки подключитесь к этому. Spring использует токен в локальном потоке для поиска объектов в области запроса (или сеанса), поэтому, если вы пытаетесь получить доступ к области запроса bean из другого потока, вероятно, что токена там не будет.