2009-10-06 7 views
15

Ho un'applicazione Web in esecuzione in tomcat dove sto utilizzando un ThreadPool (Java 5 ExecutorService) per eseguire operazioni intensive di IO in parallelo per migliorare le prestazioni. Vorrei che alcuni dei bean utilizzati all'interno di ciascun thread in pool fossero inclusi nell'ambito della richiesta, ma i Thread nel ThreadPool non hanno accesso al contesto di primavera e ottengono un errore del proxy. Qualche idea su come rendere disponibile il contesto di primavera ai thread in ThreadPool per risolvere i problemi di proxy?Accesso a bean proxy nell'ambito di Thread di

Sto indovinando che ci deve essere un modo per registrare/annullare la registrazione di ogni thread nel ThreadPool con molla per ogni attività, ma non hanno avuto fortuna a trovare come farlo.

Grazie!

risposta

45

Sto utilizzando la seguente super classe per le mie attività che devono avere accesso all'ambito della richiesta. In pratica puoi solo estenderlo e implementare la tua logica nel metodo 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

Grazie per l'aiuto Eugène. Molto apprezzato! – Perulish8

+3

Wow. Vorrei poterlo vedere come 10 altri voti. Grazie per l'aiuto. – Cameron

+2

Sembra che questo metodo abbia un deadlock, vedere http://stackoverflow.com/q/15768556/438742 – kan

0

Potresti provare al contrario? Utilizzare un contenitore di dati archiviato nell'ambito della richiesta e assegnarlo al pool di thread (eventualmente inserirlo in una coda, in modo che il pool di thread possa prendere un contenitore di dati alla volta, lavorarci sopra, contrassegnarlo come "completato" e continuare con il prossimo).

0

Spring ha una classe ThreadPoolTaskExecutor che è possibile utilizzare per gestire il pool di thread da Spring. Tuttavia, sembra che tu debba fare del lavoro per rendere disponibile il contesto Spring per ogni thread.

Non sono sicuro che funzionerà anche se lo collegherai in questo modo. Spring usa un token nel thread locale per localizzare gli oggetti nell'ambito request (o session), quindi se stai provando ad accedere a un scope scope da un thread diverso, è probabile che il token non sia lì.

+0

questo è solo un modo per creare esecutore. il vero problema qui è che 'ma i Thread nel ThreadPool non hanno accesso al contesto di primavera e ottengono un errore del proxy. – msangel

11

Vorrei anche avere 1000 voti da dare alla risposta attualmente accettata. Ero stato perplesso su come farlo per qualche tempo. Sulla base di ciò, ecco la mia soluzione che utilizza l'interfaccia Callable nel caso in cui si desideri utilizzare alcune delle nuove funzionalità di @Async 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; 
} 
+0

gr8 !! Stavo cercando questo :) – hop

+0

ok, ho un metodo con annotazione '@ Async'. è restituito 'Futuro ' risultato. Mi rendo conto che all'interno della classe generata da proxy ci sono 'ThreadExecutor' e' Callabbe ', ma come forzarlo ad usare questo tipo di Callable? Come soluzione temporanea, passo semplicemente il riferimento al contesto dell'app nel metodo, funziona per me ma non sembra buono. – msangel

+0

Penso che questo codice potrebbe avere un problema. RequestContextHolder.getRequestAttributes() restituirà la stessa istanza utilizzata nella richiesta corrente, non una copia. Pertanto, potresti accedere a un oggetto ServletRequestAttributes al di fuori dello span della richiesta servlet ... –

Problemi correlati