2009-10-06 20 views
15

Tengo una aplicación web ejecutándose en tomcat en la que estoy usando ThreadPool (Java 5 ExecutorService) para ejecutar operaciones intensivas de IO en paralelo para mejorar el rendimiento. Me gustaría que algunos de los beans utilizados en cada subproceso agrupado estén en el ámbito de solicitud, pero los subprocesos en el ThreadPool no tienen acceso al contexto de primavera y se obtiene un error de proxy. ¿Alguna idea sobre cómo hacer que el contexto de primavera esté disponible para los hilos en ThreadPool para resolver las fallas del proxy?Acceso a beans proxy de ámbito dentro de Threads of

Supongo que debe haber una manera de registrar/anular el registro de cada hilo en el ThreadPool con la primavera de cada tarea, pero no he tenido suerte para encontrar cómo hacerlo.

Gracias!

Respuesta

45

Estoy utilizando la siguiente súper clase para mis tareas que necesitan tener acceso al ámbito de solicitud. Básicamente puedes extenderlo e implementar tu lógica en el método 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

Gracias por la ayuda Eugene. ¡Muy apreciado! – Perulish8

+3

Wow. Ojalá pudiera subir esto como 10 votos más. Gracias por la ayuda. – Cameron

+2

Parece que este método tiene un interbloqueo, consulte http://stackoverflow.com/q/15768556/438742 – kan

0

¿Podría intentarlo al revés? Use un contenedor de datos que esté almacenado en el alcance de la solicitud y proporciónelo al grupo de subprocesos (tal vez póngalo en una cola para que el grupo de subprocesos pueda tomar un contenedor de datos a la vez, trabajar en él, marcarlo como "hecho" y continuar con el siguiente).

0

Spring tiene una clase ThreadPoolTaskExecutor que puede usar para administrar su grupo de subprocesos desde Spring. Sin embargo, parece que tendrías que trabajar un poco para que el contexto de Spring esté disponible para cada hilo.

No estoy seguro de si funcionará aunque lo conecte de esta manera. Spring usa un token en el subproceso local para localizar objetos en el ámbito de solicitud (o sesión), por lo que si intenta acceder a un bean de ámbito de solicitud desde un subproceso diferente, es probable que el token no esté allí.

+0

esto es solo una forma de crear ejecutor. el verdadero problema aquí es que 'pero los hilos en el ThreadPool no tienen acceso al contexto de primavera y obtienen una falla de proxy. – msangel

11

También me gustaría tener 1000 votos para dar a la respuesta actualmente aceptada. Me había quedado perplejo sobre cómo hacer esto por un tiempo. Basado en esto, aquí está mi solución usando la interfaz de llamadas en caso de que quiera usar algunas de las nuevas cosas de @Async en 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; 
} 
+0

gr8 !! Estaba buscando esto :) – hop

+0

bien, tengo el método con la anotación '@ Async'. es returnig 'Futuro ' resultado. Creo que dentro de la clase generada por proxy hay 'ThreadExecutor' y' Callabbe ', pero ¿cómo forzarlo a utilizar ese tipo de Llamado? Como solución temporal acabo de pasar la referencia de contexto de la aplicación al método. Funciona para mí, pero no se ve bien. – msangel

+0

Creo que este código puede tener un problema. RequestContextHolder.getRequestAttributes() devolverá la misma instancia utilizada en la solicitud actual, no una copia. Por lo tanto, puede estar accediendo a un objeto ServletRequestAttributes fuera del alcance de la solicitud de servlet ... –

Cuestiones relacionadas