在web項(xiàng)目中我們通常都要根據(jù)當(dāng)前用戶進(jìn)行一些操作剪菱,如果使用了一些權(quán)限框架浪耘,比如spring security或者shiro等症汹,他們都提供了一個(gè)獲取當(dāng)前登錄的用戶的方法宾肺,直接調(diào)用即可溉委,但是如果不使用相關(guān)框架,獲取用戶就略微顯得有些復(fù)雜爱榕,為了簡單,這時(shí)候ThreadLocal就能幫到我們了坡慌。
ThreadLocal提供本地線程變量黔酥。這個(gè)變量里面的值(通過get方法獲取)是和其他線程分割開來的洪橘,變量的值只有當(dāng)前線程能訪問到跪者,不像一般的類型比如Person,Student類型的變量,只要訪問到聲明該變量的對象熄求,即可訪問其全部內(nèi)容渣玲,而且各個(gè)線程的訪問的數(shù)據(jù)是無差別的
我們都知道,在web環(huán)境中弟晚,一個(gè)用戶的請求是一直在一個(gè)線程中的忘衍,ThreadLocal剛好能幫助我們做到在第一次登錄請求中的時(shí)候放入相關(guān)參數(shù),比如用戶信息卿城,在后續(xù)請求中在線程中就可以拿到參數(shù)枚钓。
ThreadLocal
這里舉一個(gè)簡單的例子,寫一個(gè)工具類瑟押,把當(dāng)前用戶和當(dāng)前請求放入ThreadLocal中搀捷,并支持存取
工具類
public class RequestHolder {
private static final ThreadLocal<User> userHolder = new ThreadLocal<>();
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<>();
public static void add(User user) {
userHolder.set(user);
}
public static void add(HttpServletRequest request) {
requestHolder.set(request);
}
public static User getCurrentUser() {
return userHolder.get();
}
public static HttpServletRequest getCurrentRequest() {
return requestHolder.get();
}
public static void remove() {
userHolder.remove();
requestHolder.remove();
}
}
如果你還需要更多常用參數(shù),可以繼續(xù)擴(kuò)展上述方法多望。以上工具類嫩舟,就可以很容易讓我們拿到user和request了氢烘。
使用
由于我們主要目的就是在請求中拿到用戶信息和請求的信息,我們可以這樣做
首先在登錄成功后家厌,將登錄信息放入session或者redis中
編寫過濾器播玖,攔截器或者切面等,判斷當(dāng)前用戶是否登錄(session或redis中是否有用戶登錄信息)像街,如果已經(jīng)登錄了黎棠,調(diào)用以下方法
RequestHolder.add(user);
RequestHolder.add(reqest);
將我們需要的參數(shù)放入ThreadLocal中,以供后續(xù)使用镰绎。
- 在該請求未關(guān)閉之前脓斩,我們在其中任意地方可以調(diào)用以下方法獲取用戶信息或當(dāng)前請求。
// 獲取當(dāng)前用戶
RequestHolder.getCurrentUser();
// 獲取當(dāng)前請求
RequestHolder.getCurrentRequest();
- 在請求的最后畴栖,一般是攔截器或aop的方法后去調(diào)用remove()去釋放資源随静。這部其實(shí)不做也可以,因?yàn)檎埱蠼Y(jié)束了線程一般會(huì)被銷毀吗讶。本地變量自然也就不存在了燎猛。
ArgumentResolver
使用了ThreadLocal獲取當(dāng)前登錄用戶的信息已經(jīng)很方便了,但是如果我們不想每次都調(diào)用靜態(tài)方法RequestHolder.getCurrentUser()獲取用戶信息照皆,在controller層中的方法中拿到用戶信息可不可以呢重绷,答案當(dāng)然是可以。
我們可以編寫一個(gè)參數(shù)解析器膜毁,在需要使用的controller方法參數(shù)中寫上相關(guān)參數(shù)昭卓,就可以更方便的獲取參數(shù)了。
編寫參數(shù)解析器
舉個(gè)簡單的例子瘟滨,我們這里編寫一個(gè)UserArgumentResolver類并實(shí)現(xiàn)HandlerMethodArgumentResolver接口的方法候醒。
在supportsParameter()方法中配置需要解析的參數(shù)(一般是類)
最后在resolveArgument()中調(diào)用上面編寫的RequestHolder.getCurrentUser()即可。
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz == User.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return RequestHolder.getCurrentUser();
}
}
在WebConfig中注冊參數(shù)解析器
以spring boot的java conf為例
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private UserArgumentResolver userArgumentResolver;
/**
*
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
/**
***
***
*/
}
使用
@GetMapping(value = "/xxx")
@ResponseBody
public Result miaoshaResult(User user) {
//user.getName();
// xxx
return Result.success();
}
使用上述方式就完成了ThreadLocal和ArgumentResolver的配合杂瘸,尤其對當(dāng)前用戶這種參數(shù)很實(shí)用倒淫,如果你還有其他需求,可以自行擴(kuò)展败玉。敌土。。
注:
- 上述測試在ubuntu16.04 lts jdk1.8 spring boot 1.5.6.RELEASE中成功
- 上述文字皆為個(gè)人看法绒怨,如有錯(cuò)誤或建議請及時(shí)聯(lián)系我