ThreadLocal(線程本地變量)通常理解爲“採用了空間換時間的設計思想,主要用來實現在多線程環境下的線程安全和保存線程上下文中的變量”。在實際的項目開發中(比如2C APP程序的服務器端程序),通常在APP調用服務端API接口的時候,需要token(登錄)驗證並且在具體的方法中可能會使用到當前登錄賬戶的更多信息(比如當前登錄賬戶的用戶ID)。以前的做法,我們喜歡把(通過token獲取)用戶登錄信息放在http請求的request請求對象中,但是這樣做是耦合性很強,比較相同的代碼重複使用。通過了解ThreadLocal的特性後,這種情況使用ThreadLocal來實現會很合適。下面我們將講解具體的使用方法。
一、利用ThreadLocal實現中間類來保存登錄信息
package com.example.demo.common.session;
/**
* 利用線程本地變量來保存登錄信息
*
* @author Horace
*
*/
public class SessionCache {
private static ThreadLocal<Session> threadLocal = new ThreadLocal<>();
public static <T extends Session> void put(T t) {
threadLocal.set(t);
}
@SuppressWarnings("unchecked")
public static <T> T get() {
return (T) threadLocal.get();
}
public static void remove() {
threadLocal.remove();
}
}
二、在程序登錄(token)認證攔截器中實現保存和刪除功能
package com.example.demo.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import com.example.demo.common.session.SessionCache;
import com.example.demo.common.session.UserSession;
public class LoginAuthInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoginAuthInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("token");
// 根據token獲取登錄信息
UserSession session = new UserSession();
session.setAccountId("test");
session.setName("測試");
SessionCache.put(session);
logger.info("請求方法方法前攔截");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
SessionCache.remove();
logger.info("請求方法方法後處理");
}
}
三、在需要當前登錄信息的方法中使用
package com.example.demo.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.common.session.SessionCache;
import com.example.demo.common.session.UserSession;
@RestController
public class TestController {
private static final Logger logger = LoggerFactory.getLogger(TestController.class);
@RequestMapping(value = "/test/session")
public String testSession(TestDto dto) {
UserSession session = SessionCache.get();
String userId = session.getAccountId();
logger.info("當前登錄用戶ID爲{}", userId);
return "success:" + userId;
}
}
總結:通過上面的簡單的3個步驟,就可以用代碼優雅的實現登錄信息的管理。當然在上面的代碼中ThreadLocal中並不是保存的單獨的一個參數,而是用泛型指定了一個Session接口(體現面向接口編程),這樣實現可以根據不同的登錄角色(比如用戶端、服務人員端等)保存不同的登錄信息。