ThreadLocal è praticamente la risposta che stai cercando.
Questa classe fornisce variabili locali del thread. Queste variabili differiscono da (tramite il metodo get o set) della corrispondente variabile dalle rispettive controparti normali.
Spring trovi RequestContextHolder
classe Holder per esporre richiesta Web sotto forma di un filo-bound RequestAttributes oppongono. La richiesta verrà ereditata da qualsiasi sottoprocesso figlio generato dal thread corrente se il flag ereditabile è impostato su su true.
Inside the class vedrete il seguente:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
E qui è il setter reale (nota è statico):
/**
* Bind the given RequestAttributes to the current thread.
* @param attributes the RequestAttributes to expose,
* or {@code null} to reset the thread-bound context
* @param inheritable whether to expose the RequestAttributes as inheritable
* for child threads (using an {@link InheritableThreadLocal})
*/
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {}
Quindi, come si può vedere, nessuna magia lì, solo una variabile specifica del thread, fornita da ThreadLocal
.
Se siete curiosi abbastanza qui è ThreadLocal.get
implementazione (whic restituisce il valore nella copia del thread corrente di questa variabile thread-local):
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
Come si può vedere si basa semplicemente sulla ThreadLocalMap
:
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
getEntry()
esegue una ricerca all'interno della mappa. Spero che tu veda l'intera immagine ora.
Per quanto riguarda il potenziale NullPointerException
In sostanza, è possibile chiamare i metodi del proxy solo se il campo di applicazione è attiva, il che significa che l'esecuzione di thread deve essere una richiesta servlet.Quindi qualsiasi lavoro asincrono, comandi, ecc fallirà con questo approccio.
Direi, questo è un grosso problema dietro ScopedProxy
. Lo fa risolvere alcuni problemi in modo trasparente (semplifica catena di chiamate, per examaple), ma se non si seguono le regole probabilmente otterrete java.lang.IllegalStateException: No thread-bound request found
(Spring Framework Reference Documentation) dice quanto segue:
DispatcherServlet, RequestContextListener e RequestContextFilter all fanno esattamente la stessa cosa, ovvero legano l'oggetto richiesta HTTP al thread che sta servendo quella richiesta. Questo rende i bean che sono richiesti e con ambito di sessione disponibili più in basso nella catena di chiamate.
è anche possibile controllare la seguente questione: Accessing request scoped beans in a multi-threaded web application
@Async e la richiesta di attributi iniezione
In generale, non v'è alcun modo semplice per risolvere il problema. Come mostrato in precedenza, abbiamo RequestAttributes con associazione a thread.
La soluzione potenziale è passare manualmente l'oggetto richiesto e assicurarsi che la logica sottostante @Async
tenga conto di ciò.
Una soluzione un po 'più intelligente (suggerita da Eugene Kuleshov) è quella di farlo in modo trasparente. Copierò il codice per semplificare la lettura e inserire il link sotto il blocco di codice.
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();
}
qui è che domanda: Accessing scoped proxy beans within Threads of
Come si può vedere, questa soluzione si basa sul fatto costruttore verrà eseguito nel giusto contesto, per cui è possibile memorizzare nella cache giusto contesto e iniettare in un secondo momento.
Ecco un altro, piuttosto interessante, argomento @Async annotated method hanging on session-scoped bean
Cari downvoter, potresti spiegare che cosa è male appartiene alla domanda? Potrei riscriverlo in una forma più adatta. –