使用ThreadLocal和ArgumentResolver方便開發(fā)

在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ò)展上述方法多望。以上工具類嫩舟,就可以很容易讓我們拿到userrequest了氢烘。

使用

由于我們主要目的就是在請求中拿到用戶信息和請求的信息,我們可以這樣做

  1. 首先在登錄成功后家厌,將登錄信息放入session或者redis中

  2. 編寫過濾器播玖,攔截器或者切面等,判斷當(dāng)前用戶是否登錄(session或redis中是否有用戶登錄信息)像街,如果已經(jīng)登錄了黎棠,調(diào)用以下方法

RequestHolder.add(user);
RequestHolder.add(reqest);

將我們需要的參數(shù)放入ThreadLocal中,以供后續(xù)使用镰绎。

  1. 在該請求未關(guān)閉之前脓斩,我們在其中任意地方可以調(diào)用以下方法獲取用戶信息或當(dāng)前請求。
// 獲取當(dāng)前用戶
RequestHolder.getCurrentUser();

// 獲取當(dāng)前請求
RequestHolder.getCurrentRequest();
  1. 在請求的最后畴栖,一般是攔截器或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)系我
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纯赎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子南蹂,更是在濱河造成了極大的恐慌犬金,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晚顷,居然都是意外死亡峰伙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門该默,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞳氓,“玉大人,你說我怎么就攤上這事栓袖∠徽” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵裹刮,是天一觀的道長音榜。 經(jīng)常有香客問我,道長捧弃,這世上最難降的妖魔是什么赠叼? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮违霞,結(jié)果婚禮上嘴办,老公的妹妹穿的比我還像新娘。我一直安慰自己买鸽,他們只是感情好涧郊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眼五,像睡著了一般底燎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弹砚,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音枢希,去河邊找鬼桌吃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛苞轿,可吹牛的內(nèi)容都是我干的茅诱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼搬卒,長吁一口氣:“原來是場噩夢啊……” “哼瑟俭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起契邀,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤摆寄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體微饥,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逗扒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欠橘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矩肩。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肃续,靈堂內(nèi)的尸體忽然破棺而出黍檩,到底是詐尸還是另有隱情,我是刑警寧澤始锚,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布刽酱,位于F島的核電站,受9級特大地震影響疼蛾,放射性物質(zhì)發(fā)生泄漏肛跌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一察郁、第九天 我趴在偏房一處隱蔽的房頂上張望衍慎。 院中可真熱鬧,春花似錦皮钠、人聲如沸稳捆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乔夯。三九已至,卻和暖如春款侵,著一層夾襖步出監(jiān)牢的瞬間末荐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工新锈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甲脏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓妹笆,卻偏偏與公主長得像块请,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子拳缠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容