最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于Spring MVC在Controller層中注入request的坑詳解

 更新時間:2018年04月10日 09:56:36   作者:sluggarddd  
這篇文章主要給大家介紹了關(guān)于Spring MVC在Controller層中注入request的坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

前言

記一次為了節(jié)省代碼沒有在方法體中聲明HttpServletRequest,而用autowire直接注入所鉆的坑

結(jié)論:給心急的人。 直接在Controller的成員變量上使用@Autowire聲明HttpServletRequest,這是線程安全的!

@Controller
public class TestController{
 @Autowire
 HttpServletRequest request;
 @RequestMapping("/")
 public void test(){
  request.getAttribute("uid"); 
 }
}

結(jié)論如上。

背景

是這樣的,由于項目中我在Request的頭部加入身份驗證信息,而我在攔截器截獲信息并且驗證通過后,會將當前用戶的身份加到request的Attribute中,方便在Controller層拿出來復用。

疑問:為什么不直接在Controller上使用@RequestHeader取出來呢? 因為header里面是加密后的數(shù)據(jù),且要經(jīng)過一些復雜的身份驗證判斷,所以直接將這一步直接丟在了攔截器執(zhí)行。

所以當解密后,我將用戶信息(如uid)用request.setAttribute()設(shè)入request中在Controller提取。

而如果需要使用request,一般需要在方法上聲明,如:

public Result save(HttpServletRequest request){
 // dosomething();
}

那么我每個方法都要用到uid的豈不是每個方法都要聲明一個request參數(shù),為了節(jié)省著個冗余步驟。我寫了一個基類。

public class CommonController{
 @Autowire
 HttpServletReqeust request;
 public String getUid(){
  return (String)request.getAttribute("uid");
 }
}

后來我就擔心,因為controller是單例的,這么寫會不會導致后面的reqeust覆蓋前面的request,在并發(fā)條件下有線程安全問題。 于是我就到segmentFault上提問,大部分網(wǎng)友說到,確實有線程問題!segmentFault問題地址 ###驗證過程 因為網(wǎng)友大部分的觀點是只能在方法上聲明,我自然不想就此放棄多寫那么多代碼,于是開始我的驗證過程。 熱心的程序員們給我提供了好幾種解決方案,我既然花力氣證明了,就把結(jié)果放在這里,分享給大家。

方法1

第一個方法就是在controller的方法中顯示聲明HttpServletReqeust,代碼如下:

@RequestMapping("/test")
@RestController
public class CTest {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/iiii")
 public String test(HttpServletRequest request) {
  logger.info(request.hashCode() + "");
  return null;
 }
}

在瀏覽器狂按F5

輸出

當時我是懵逼的,**說好的線程安全呢!**這特么不是同一個request嗎!特么的在逗我! 為此我還找了很久request是不是重寫了hashcode()!

啊,事實是這樣的,因為我用瀏覽器狂按F5,再怎么按他也是模擬不了并發(fā)的。那么就相當于,服務(wù)器一直在用同一個線程處理我的請求就足夠了,至于這個request的hashcode,按照jdk的說法是根據(jù)obj在jvm的虛擬地址計算的,后面的事情是我猜的,如果有知道真正真想的還望告知!

猜測

服務(wù)器中每個thread所申請的request的內(nèi)存空間在這個服務(wù)器啟動的時候就是固定的,那么我每次請求,他都會在他所申請到的內(nèi)存空間(可能是類似數(shù)組這樣的結(jié)構(gòu))中新建一個request,(類似于數(shù)組的起點總是同一個內(nèi)存地址),那么我發(fā)起一個請求,他就會在起始位置新建一個Request傳遞給Servlet并開始處理,處理結(jié)束后就會銷毀,那么他下一個請求所新建的Request,因為之前的request銷毀了,所以又從起始地址開始創(chuàng)建,這樣一切就解釋得通了!

猜測完畢

驗證猜想:

我不讓他有銷毀的時間不就可以了嗎 測試代碼

@RequestMapping("/test")
@RestController
public class CTest {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/oooo")
 public String testA(HttpServletRequest request) throws Exception {
  Thread.sleep(3000);
  logger.info(request.hashCode() + "");
  logger.info(reqeust.getHeader("uid");
  return null;
 }
 @RequestMapping("/iiii")
 public String test(HttpServletRequest request) {
  logger.info(request.hashCode() + "");
  logger.info(reqeust.getHeader("uid");
  return null;
 }
}

如上,我在接口/oooo中休眠3秒,如果他是共用一個reqeust的話,那么后面的請求將覆蓋這個休眠中的reqeust,所傳入的uid即為接口地址。先發(fā)起/oooo后發(fā)起/iiii

輸出

controller.CTest:33 - 364716268
controller.CTest:34 - iiii
controller.CTest:26 - 1892130707
controller.CTest:27 - oooo

結(jié)論: 1、后發(fā)起的/iiii沒有覆蓋前面/oooo的數(shù)據(jù),沒有線程安全問題。 2、request的hashcode不一樣,因為/oooo的阻塞,導致另一個線程需要去處理,所以他新建了request,而不是向之前一樣全部hashcode相同。

二輪驗證

public class HttpTest {
 public static void main(String[] args) throws Exception {
  for (int i = 300; i > 0; i--) {
   final int finalI = i;
   new Thread() {
    @Override
    public void run() {
     System.out.println("v###" + finalI);
     HttpRequest.get("http://localhost:8080/test/iiii?").header("uid", "v###" + finalI).send();
    }
   }.start();
  }
 }
}

在模擬并發(fā)條件下,header中的uid300個完全接受,沒有覆蓋

所以這種方式,沒有線程安全問題。

方法2

在CommonController中,使用@ModelAttribute處理。

public class CommonController {

// @Autowired
 protected HttpServletRequest request;
 @ModelAttribute
 public void bindreq(HttpServletRequest request) {
  this.request = request;
 }
 protected String getUid() {
  System.out.println(request.toString());
  return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid");
 }
}

這樣子是有線程安全問題的!后面的request有可能覆蓋掉之前的!

驗證代碼

@RestController
@RequestMapping("/test")
public class CTest extends CommonController {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/iiii")
 public String test() {
  logger.info(request.getHeader("uid"));
  return null;
 }
}
public class HttpTest {
 public static void main(String[] args) throws Exception {
  for (int i = 100; i > 0; i--) {
   final int finalI = i;
   new Thread() {
    @Override
    public void run() {
     System.out.println("v###" + finalI);
     HttpRequest.get("http://localhost:8080/test/iiii").header("uid", "v###" + finalI).send();
    }
   }.start();
  }
 }
}

截取了部分輸出結(jié)果

controller.CTest:26 - v###52
controller.CTest:26 - v###13
controller.CTest:26 - v###57
controller.CTest:26 - v###57
controller.CTest:26 - v###21
controller.CTest:26 - v###10
controller.CTest:26 - v###82
controller.CTest:26 - v###82
controller.CTest:26 - v###93
controller.CTest:26 - v###71
controller.CTest:26 - v###71
controller.CTest:26 - v###85
controller.CTest:26 - v###85
controller.CTest:26 - v###14
controller.CTest:26 - v###47
controller.CTest:26 - v###47
controller.CTest:26 - v###69
controller.CTest:26 - v###22
controller.CTest:26 - v###55
controller.CTest:26 - v###61

可以看到57、71、85、47被覆蓋了,丟失了部分request!

這么做是線程不安全的!

方法3

使用CommonController作為基類,將request Autowire。

public class CommonController {
 @Autowired
 protected HttpServletRequest request;
 protected String getUid() {
  System.out.println(request.toString());
  return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid");
 }
}

測試接口同上,結(jié)果喜人! 100個request沒有任何覆蓋,我加大范圍測了五六次,上千次請求沒一個覆蓋,可以證明這種寫法沒有線程安全問題了!

另外還有一點有趣的是,無論使用多少并發(fā),request的hashcode始終是相同的,而且,測試同一個Controller中不同的接口,他也相同,使用sleep強行阻塞,hashcode也是相同。但是訪問不同的controller,hashcode卻是不同的,具體里面如何實現(xiàn)我也就沒有繼續(xù)深挖了。

但是結(jié)論是出來的,就如文章最開始所說一樣。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 使用Java8進行分組(多個字段的組合分組)

    使用Java8進行分組(多個字段的組合分組)

    本文主要介紹了使用Java8進行分組(多個字段的組合分組),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • 使用mtrace追蹤JVM堆外內(nèi)存泄露的方法

    使用mtrace追蹤JVM堆外內(nèi)存泄露的方法

    這篇文章主要給大家介紹了如何使用mtrace追蹤JVM堆外內(nèi)存泄露,文章通過代碼示例介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2023-09-09
  • java  Lock接口詳解及實例代碼

    java Lock接口詳解及實例代碼

    這篇文章主要介紹了java Lock接口詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • java中對象和JSON格式的轉(zhuǎn)換方法代碼

    java中對象和JSON格式的轉(zhuǎn)換方法代碼

    JSON格式可以輕松地以面向?qū)ο蟮姆绞睫D(zhuǎn)換為Java對象,下面這篇文章主要給大家介紹了關(guān)于java中對象和JSON格式的轉(zhuǎn)換方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-12-12
  • Java日常練習題,每天進步一點點(13)

    Java日常練習題,每天進步一點點(13)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07
  • JavaBean字段如何防止非空賦值

    JavaBean字段如何防止非空賦值

    這篇文章主要介紹了JavaBean字段如何防止非空賦值的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java線程公平鎖和非公平鎖的差異講解

    Java線程公平鎖和非公平鎖的差異講解

    今天小編就為大家分享一篇關(guān)于Java線程公平鎖和非公平鎖的差異講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • 基于JPA的Repository使用詳解

    基于JPA的Repository使用詳解

    這篇文章主要介紹了JPA的Repository使用詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java 遍歷Map的幾種方法總結(jié)

    java 遍歷Map的幾種方法總結(jié)

    這篇文章主要介紹了java 遍歷Map的四種方法,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
    2020-10-10
  • MyBatis詳細講解DAO代理的使用

    MyBatis詳細講解DAO代理的使用

    MyBatis允許只聲明一個dao接口,而無需寫dao實現(xiàn)類的方式實現(xiàn)數(shù)據(jù)庫操作。前提是必須保證Mapper文件中的<mapper>標簽的namespace屬性值必須要和dao接口的類路徑一致,MyBatis容器會自動通過動態(tài)代理生成接口的實現(xiàn)類
    2022-04-04

最新評論

衡阳市| 许昌市| 彭山县| 长葛市| 临朐县| 呼玛县| 广元市| 洛川县| 棋牌| 丰宁| 长沙市| 玉林市| 五指山市| 博野县| 定襄县| 小金县| 芜湖县| 历史| 西贡区| 盈江县| 谷城县| 永新县| 舟曲县| 巫溪县| 香港 | 洪洞县| 佛冈县| 鲁山县| 洪泽县| 马山县| 甘泉县| 手游| 肇东市| 遂川县| 徐水县| 白玉县| 绿春县| 库车县| 泰顺县| 桓台县| 新余市|