ThreadLocal幾乎是你正在尋找的答案。
該類提供線程局部變量。這些變量與它們的正常副本不同,它們與 不同,因爲訪問一個 (通過其get或set方法)的每個線程都有其自己的獨立初始化變量 副本。
Spring有RequestContextHolder
Holder類以暴露在線裝 RequestAttributes對象的形式的web請求。如果可繼承標誌 設置爲true,則該請求將由當前線程產生的任何子代 線程繼承。
Inside the class你會看到以下內容:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
這裏是實際的二傳手(注意它是靜態的):
/**
* 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) {}
所以,你可以看到,沒有魔法那裏,只是一個線程特定的變量,由ThreadLocal
提供。
如果你足夠這裏古玩是ThreadLocal.get
實現(whic在此線程局部變量的當前線程副本返回值):
/**
* 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();
}
正如你可以看到它簡單地依賴於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()
在Map中執行查找。我希望你現在看到整個畫面。
關於潛在的NullPointerException
基本上,你可以調用代理的方法只有範圍是積極的,這意味着執行的線程應該是一個servlet請求。所以任何異步作業,Commands等都會失敗。
我會說,這是ScopedProxy
背後的一個相當大的問題。它確實解決了一些問題,透明地(簡化了調用鏈,爲examaple),但是如果你不遵守規則,你可能會得到java.lang.IllegalStateException: No thread-bound request found
(Spring Framework Reference Documentation)說以下內容:
的DispatcherServlet,RequestContextListener和RequestContextFilter全部都是 ,它們完全相同,即將HTTP請求對象綁定到服務該請求的 線程。這使得請求和會話範圍爲 的bean可以進入呼叫鏈的更遠處。
您還可以檢查以下問題:Accessing request scoped beans in a multi-threaded web application
@Async和請求屬性注入
一般來說,有解決問題沒有簡單的方法。如前所示,我們有線程綁定的RequestAttributes。
可能的解決方案是手動傳遞所需的對象,並確保@Async
背後的邏輯將此考慮在內。
一個更聰明的解決方案(由Eugene Kuleshov建議)是透明地做到這一點。我將複製代碼以簡化閱讀並將鏈接放在代碼塊下。
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();
}
這裏是一個問題:Accessing scoped proxy beans within Threads of
正如你所看到的,這個解決方案依賴於事實的構造將在適當的上下文中執行,因此可以緩存適當的環境,後來它注入。
這裏是另一個非常有趣,主題@Async annotated method hanging on session-scoped bean
親愛的downvoter,你能解釋一下屬於這個問題的不好嗎?我可能會把它改寫成更合適的形式。 –