在使用Spring MVC開發web項目時,在一個請求的任意階段,都可以通過RequestContextHolder.getRequestAttributes()獲取RequsetAttributes對象,進而獲取request對象。這是怎么實現的呢?帶着這個疑問,我們一起理解一下ThreadLocal對象。
首先看一下getRequestAttributes()的實現
public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; }
這里的requestAttributesHolder是RequestContextHolder類的一個成員變量,它的定義是
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
可見它是一個ThreadLocal對象。那么ThreadLocal對象是做什么的呢?一般我們稱它本地線程變量,是一個變量在線程中存儲的一個副本。我們不妨先看一下它的源代碼。
先看一下requestAttributesHolder.get()中用到的get()函數。代碼如下
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(); }
首先獲取當前線程,然后getMap(t),這一步做了什么呢?看代碼
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; 3 }
即獲取線程對象t中的成員變量threadLocals,這個變量在Thread對象中的定義為
ThreadLocal.ThreadLocalMap threadLocals = null;
我們接着上面往下看。當map!=null時,以當前ThreadLocal對象為key從map中取值。若map==null,則返回setInitialValue()。再看下
setInitialValue():
1 private T setInitialValue() { 2 T value = initialValue(); 3 Thread t = Thread.currentThread(); 4 ThreadLocalMap map = getMap(t); 5 if (map != null) 6 map.set(this, value); 7 else
8 createMap(t, value); 9 return value; 10 }
protected T initialValue() { return null; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
至此,相信讀者應該有一個大概的思路了。這里setInitialValue()做的事,先通過初始化方法獲取初始化值,然后獲取當前線程的threadLocals對象,若threadLocals不為空則為當前ThreadLocal
對象賦予初始值,否則新建一個threadLocals對象。
除了get()方法,還有一個set()方法。代碼如下
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
同樣先獲取當前線程的成員變量threadLocals,若不為空則以當前對象為key賦值,若為空則新建一個threadLocals,通過構造器賦值。
這時候再看看文章一開始提到的requestAttributesHolder變量,它是一個static final對象,意味着它是一個全局常量,那么在當前線程中的任意位置,用requestAttributesHolder.get()
方法獲取存儲在Thread的threadLocals對象中的值,都是可以獲取到想要的值的。
總結一下,ThreadLocal對象是為了在一個線程執行的任意時間內獲取某個值而創建的變量副本,這個副本存在當前線程的一個成員變量threadLocals里,存儲的數據結構類似於key-value,
只不過key為ThreadLocal對象而已。第一篇技術博客寫的比較青澀,還望讀者多多見諒。
參考資料
1. JDK7 源碼
作者:
mayday芋頭
本博客中未標明轉載的文章歸作者mayday芋頭和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。