今天下午在看代码的时候,发现Spring中写了一个切面方法,来验证请求的token头。
中间获取每次请求的request对象时,并没有从controller中获取,而是直接用了一个类去获取request,当时有些疑惑,直接获取如何保证每次获取的request对象是当前的请求对象。
于是搜索了一番相关文章,对于@controller还有ThreadLoal有了一定的理解,所以在此记录一下。
在Service中直接获取request的方式,如下
public static HttpServletRequest getRequest() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
return request;
}
每次请求都会产生一个新的线程,而requestAttributes是一个ThreadLocal属性,属于线程内变量,各个线程之间互不干扰。
自己推断了一下,每次RequestContextHolder,getRequestAttributes获取的都是当前执行的请求线程的requestAttributes,也就获取了当前请求的request。
要弄明白这个问题首先就得弄懂ThreadLocal的用法。
ThreadLocal内部有一个ThreadlLocalMap内部类,每个Thread线程内部都有一个ThreadLocalMap对象。
每创建一个ThreadLocal变量,都会在调用这个属性的Thread的ThreadLocalMap中存入一个副本。
key值为ThreadLocal,value为实际使用的变量
每次取用的时候,会根据当前线程,去取当前线程中的ThreadLocalMap,然后取出这个线程变量。
如下为ThreadLocal的get()
/**
* 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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
getMap()方法取出线程中的ThreadLocalMap
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
createMap()创建当前线程的ThreadLocalMap
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
同时Thread中的ThreadLocalMap属性如下: