2014-05-19 24 views
12

Nella mia app ho alcuni servizi web asincroni. Il server accetta la richiesta, restituisce la risposta OK e avvia la richiesta di elaborazione con AsyncTaskExecutor. La mia domanda è come abilitare richiesta portata qui perché in questo processo ho bisogno di ottenere classe che viene annotata da:Come abilitare lo scope di richiesta nell'esecutore task asincrono

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) 

Ora ho eccezione:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestContextImpl': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. 

perché si corre in SimpleAsyncTaskExecutor e non in DispatcherServlet

mia elaborazione asincrona di richiesta

taskExecutor.execute(new Runnable() { 

    @Override 
    public void run() { 
     asyncRequest(request); 
    } 
}); 

dove TaskExecutor è:

<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" /> 

risposta

35

Abbiamo avuto lo stesso problema, necessario per eseguire codice in background utilizzando @Async, quindi non è stato possibile utilizzare alcun bean Session o RequestScope. Abbiamo risolto nel seguente modo:

  • Creare un TaskPoolExecutor personalizzato che memorizza scope informazioni con i compiti
  • Creare una Callable speciale (o Runnable), che utilizza le informazioni per impostare e cancellare il contesto per il thread in background
  • Creazione di una configurazione di override di utilizzare l'esecutore personalizzato

Nota: questo funziona solo per la sessione e scope richiesta fagioli, e non per contesto di sicurezza (come nella Primavera di sicurezza). Dovresti usare un altro metodo per impostare il contesto di sicurezza se questo è ciò che stai cercando.

Nota2: per brevità, viene mostrata solo l'implementazione Callable e submit(). Puoi fare lo stesso per Runnable ed execute().

Ecco il codice:

Esecutore:

public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor { 
    @Override 
    public <T> Future<T> submit(Callable<T> task) { 
     return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes())); 
    } 

    @Override 
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) { 
     return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes())); 
    } 
} 

Callable:

public class ContextAwareCallable<T> implements Callable<T> { 
    private Callable<T> task; 
    private RequestAttributes context; 

    public ContextAwareCallable(Callable<T> task, RequestAttributes context) { 
     this.task = task; 
     this.context = context; 
    } 

    @Override 
    public T call() throws Exception { 
     if (context != null) { 
      RequestContextHolder.setRequestAttributes(context); 
     } 

     try { 
      return task.call(); 
     } finally { 
      RequestContextHolder.resetRequestAttributes(); 
     } 
    } 
} 

Configurazione:

@Configuration 
public class ExecutorConfig extends AsyncConfigurerSupport { 
    @Override 
    @Bean 
    public Executor getAsyncExecutor() { 
     return new ContextAwarePoolExecutor(); 
    } 
} 
+0

Ha funzionato per me. Devono essere preservati solo i valori di ThreadLocal o MDC. Quindi risolvere il problema. –

+0

Ho appena usato circa la stessa soluzione per SecurtyContext. Questa è una soluzione così bella e pulita per questo problema! Grazie per la condivisione! –

+0

Con Spring 4 (.3.8) questo non sembra funzionare più. L'eccezione è "richiesta Scope" non è attiva per il thread corrente, si consideri la definizione di un proxy con scope per questo bean se si intende fare riferimento ad esso da un singleton, l'eccezione nidificata è java.lang.IllegalStateException: Can not ask for attribute attribute - la richiesta non è più attiva! " –

8

Non v'è alcun modo per ottenere una richiesta oggetto ambito in un thread figlio asincrona, dal momento che il thread di richiesta genitore elaborazione originale potrebbe essere già impegnato la risposta al client e tutti gli oggetti di richiesta sono distrutto. Un modo per gestire tali scenari consiste nell'utilizzare l'ambito personalizzato, ad esempio SimpleThreadScope.

un problema con SimpleThreadScope è che i thread figlio non erediteranno le variabili dell'ambito genitori, perché utilizza internamente ThreadLocal semplice. Per superare questo, implementare un ambito personalizzato che è esattamente simile a SimpleThreadScope ma utilizza InheritableThreadLocal internamente. Per maggiori informazioni reg questo Spring MVC: How to use a request-scoped bean inside a spawned thread?

+0

ehi, sai come siamo in grado di mantenere la richiesta oggetto dopo che l'async è stato trigso Gered. Sto perdendo l'oggetto richiesta non appena la richiesta è completata, ma asincrono è ancora in corso. Potrei conservare l'attributo request utilizzando RequestAttributeHolder ma la richiesta all'interno dell'attributo sta perdendo tutte le proprietà come URI, Intestazioni, Attributi correlati alla richiesta. Qualsiasi opzione da mantenere, sia creando una nuova copia che conservando o clonando in quel modo. –

+0

Il modo più semplice è passare semplicemente gli attributi richiesti relativi alla richiesta come parametro del metodo di input, mentre si richiama l'attività asincrona – Thilak

+0

In questo momento, sto usando solo questo approccio. Grazie per la tua risposta. –

Problemi correlati