成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringMVC 中的參數還能這么傳遞?漲姿勢了!

開發 前端
今天來聊一個 JavaWeb 中簡單的話題,但是感覺卻比較稀罕,因為這個技能點,有的小伙伴們可能沒聽過!

[[384541]]

今天來聊一個 JavaWeb 中簡單的話題,但是感覺卻比較稀罕,因為這個技能點,有的小伙伴們可能沒聽過!

1.緣起

說到 Web 請求參數傳遞,大家能想到哪些參數傳遞方式?

參數可以放在地址欄中,不過地址欄參數的長度有限制,并且在有的場景下我們可能不希望參數暴漏在地址欄中。參數可以放在請求體中,這個沒啥好說的。

小伙伴們試想這樣一個場景:

在一個電商項目中,有一個提交訂單的請求,這個請求是一個 POST 請求,請求參數都在請求體中。當用戶提交成功后,為了防止用戶刷新瀏覽器頁面造成訂單請求重復提交,我們一般會將用戶重定向到一個顯示訂單的頁面,這樣即使用戶刷新頁面,也不會造成訂單請求重復提交。

大概的代碼就像下面這樣:

  1. @Controller 
  2. public class OrderController { 
  3.     @PostMapping("/order"
  4.     public String order(OrderInfo orderInfo) { 
  5.         //其他處理邏輯 
  6.         return "redirect:/orderlist"
  7.     } 

這段代碼我相信大家都懂吧!如果不懂可以看看松哥錄制的免費的 SpringMVC 入門教程(硬核!松哥又整了一套免費視頻,搞起!)。

但是這里有一個問題:如果我想傳遞參數怎么辦?

如果是服務器端跳轉,我們可以將參數放在 request 對象中,跳轉完成后還能拿到參數,但是如果是客戶端跳轉我們就只能將參數放在地址欄中了,像上面這個方法的返回值我們可以寫成:return "redirect:/orderlist?xxx=xxx";,這種傳參方式有兩個缺陷:

  • 地址欄的長度是有限的,也就意味著能夠放在地址欄中的參數是有限的。
  • 不想將一些特殊的參數放在地址欄中。

那該怎么辦?還有辦法傳遞參數嗎?

有!這就是今天松哥要和大家介紹的 flashMap,專門用來解決重定向時參數的傳遞問題。

2.flashMap

在重定向時,如果需要傳遞參數,但是又不想放在地址欄中,我們就可以通過 flashMap 來傳遞參數,松哥先來一個簡單的例子大家看看效果:

首先我們定義一個簡單的頁面,里邊就一個 post 請求提交按鈕,如下:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"
  5.     <title>Title</title> 
  6. </head> 
  7. <body> 
  8. <form action="/order"
  9.     <input type="submit" value="提交"
  10. </form> 
  11. </body> 
  12. </html> 

然后在服務端接收該請求,并完成重定向:

  1. @Controller 
  2. public class OrderController { 
  3.     @PostMapping("/order"
  4.     public String order(HttpServletRequest req) { 
  5.         FlashMap flashMap = (FlashMap) req.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE); 
  6.         flashMap.put("name""江南一點雨"); 
  7.         return "redirect:/orderlist"
  8.     } 
  9.  
  10.     @GetMapping("/orderlist"
  11.     @ResponseBody 
  12.     public String orderList(Model model) { 
  13.         return (String) model.getAttribute("name"); 
  14.     } 

首先在 order 接口中,獲取到 flashMap 屬性,然后存入需要傳遞的參數,這些參數最終會被 SpringMVC 自動放入重定向接口的 Model 中,這樣我們在 orderlist 接口中,就可以獲取到該屬性了。

當然,這是一個比較粗糙的寫法,我們還可以通過 RedirectAttributes 來簡化這一步驟:

  1. @Controller 
  2. public class OrderController { 
  3.     @PostMapping("/order"
  4.     public String order(RedirectAttributes attr) { 
  5.         attr.addFlashAttribute("site""www.javaboy.org"); 
  6.         attr.addAttribute("name""微信公眾號:江南一點雨"); 
  7.         return "redirect:/orderlist"
  8.     } 
  9.  
  10.     @GetMapping("/orderlist"
  11.     @ResponseBody 
  12.     public String orderList(Model model) { 
  13.         return (String) model.getAttribute("site"); 
  14.     } 

RedirectAttributes 中有兩種添加參數的方式:

  • addFlashAttribute:將參數放到 flashMap 中。
  • addAttribute:將參數放到 URL 地址中。

經過前面的講解,現在小伙伴們應該大致明白了 flashMap 的作用了,就是在你進行重定向的時候,不通過地址欄傳遞參數。

很多小伙伴可能會有疑問,重定向其實就是瀏覽器發起了一個新的請求,這新的請求怎么就獲取到上一個請求保存的參數呢?這我們就要來看看 SpringMVC 的源碼了。

3.源碼分析

首先這里涉及到一個關鍵類叫做 FlashMapManager,如下:

  1. public interface FlashMapManager { 
  2.  @Nullable 
  3.  FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response); 
  4.  void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response); 

兩個方法含義一眼就能看出來:

retrieveAndUpdate:這個方法用來恢復參數,并將恢復過的的參數和超時的參數從保存介質中刪除。

saveOutputFlashMap:將參數保存保存起來。

FlashMapManager 的實現類如下:

從這個繼承類中,我們基本上就能確定默認的保存介質時 session。具體的保存邏輯則是在 AbstractFlashMapManager 類中。

整個參數傳遞的過程可以分為三大步:

第一步,首先我們將參數設置到 outputFlashMap 中,有兩種設置方式:我們前面的代碼 req.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE) 就是直接獲取 outputFlashMap 對象然后把參數放進去;第二種方式就是通過在接口中添加 RedirectAttributes 參數,然后把需要傳遞的參數放入 RedirectAttributes 中,這樣當處理器處理完畢后,會自動將其設置到 outputFlashMap 中,具體邏輯在 RequestMappingHandlerAdapter#getModelAndView 方法中:

  1. private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, 
  2.   ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { 
  3.  //省略... 
  4.  if (model instanceof RedirectAttributes) { 
  5.   Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); 
  6.   HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 
  7.   if (request != null) { 
  8.    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); 
  9.   } 
  10.  } 
  11.  return mav; 

可以看到,如果 model 是 RedirectAttributes 的實例的話,則通過 getOutputFlashMap 方法獲取到 outputFlashMap 屬性,然后相關的屬性設置進去。

這是第一步,就是將需要傳遞的參數,先保存到 flashMap 中。

第二步,重定向對應的視圖是 RedirectView,在它的 renderMergedOutputModel 方法中,會調用 FlashMapManager 的 saveOutputFlashMap 方法,將 outputFlashMap 保存到 session 中,如下:

  1. protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, 
  2.   HttpServletResponse response) throws IOException { 
  3.  String targetUrl = createTargetUrl(model, request); 
  4.  targetUrl = updateTargetUrl(targetUrl, model, request, response); 
  5.  // Save flash attributes 
  6.  RequestContextUtils.saveOutputFlashMap(targetUrl, request, response); 
  7.  // Redirect 
  8.  sendRedirect(request, response, targetUrl, this.http10Compatible); 

RequestContextUtils.saveOutputFlashMap 方法最終就會調用到 FlashMapManager 的 saveOutputFlashMap 方法,將 outputFlashMap 保存下來。我們來大概看一下保存邏輯:

  1. public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) { 
  2.  if (CollectionUtils.isEmpty(flashMap)) { 
  3.   return
  4.  } 
  5.  String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request); 
  6.  flashMap.setTargetRequestPath(path); 
  7.  flashMap.startExpirationPeriod(getFlashMapTimeout()); 
  8.  Object mutex = getFlashMapsMutex(request); 
  9.  if (mutex != null) { 
  10.   synchronized (mutex) { 
  11.    List<FlashMap> allFlashMaps = retrieveFlashMaps(request); 
  12.    allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<>()); 
  13.    allFlashMaps.add(flashMap); 
  14.    updateFlashMaps(allFlashMaps, request, response); 
  15.   } 
  16.  } 
  17.  else { 
  18.   List<FlashMap> allFlashMaps = retrieveFlashMaps(request); 
  19.   allFlashMaps = (allFlashMaps != null ? allFlashMaps : new ArrayList<>(1)); 
  20.   allFlashMaps.add(flashMap); 
  21.   updateFlashMaps(allFlashMaps, request, response); 
  22.  } 

其實這里的邏輯也很簡單,保存之前會給 flashMap 設置兩個屬性,一個是重定向的 url 地址,另一個則是過期時間,過期時間默認 180 秒,這兩個屬性在第三步加載 flashMap 的時候會用到。然后將 flashMap 放入集合中,并調用 updateFlashMaps 方法存入 session 中。

第三步,當重定向請求到達 DispatcherServlet#doService 方法后,此時會調用 FlashMapManager#retrieveAndUpdate 方法從 Session 中獲取 outputFlashMap 并設置到 Request 屬性中備用(最終會被轉化到 Model 中的屬性),相關代碼如下:

  1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 
  2.  //省略... 
  3.  if (this.flashMapManager != null) { 
  4.   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 
  5.   if (inputFlashMap != null) { 
  6.    request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 
  7.   } 
  8.   request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 
  9.   request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 
  10.  } 
  11.  //省略... 

注意這里獲取出來的 outputFlashMap 換了一個名字,變成了 inputFlashMap,其實是同一個東西。

我們可以大概看一下獲取的邏輯 AbstractFlashMapManager#retrieveAndUpdate:

  1. public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) { 
  2.  List<FlashMap> allFlashMaps = retrieveFlashMaps(request); 
  3.  if (CollectionUtils.isEmpty(allFlashMaps)) { 
  4.   return null
  5.  } 
  6.  List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps); 
  7.  FlashMap match = getMatchingFlashMap(allFlashMaps, request); 
  8.  if (match != null) { 
  9.   mapsToRemove.add(match); 
  10.  } 
  11.  if (!mapsToRemove.isEmpty()) { 
  12.   Object mutex = getFlashMapsMutex(request); 
  13.   if (mutex != null) { 
  14.    synchronized (mutex) { 
  15.     allFlashMaps = retrieveFlashMaps(request); 
  16.     if (allFlashMaps != null) { 
  17.      allFlashMaps.removeAll(mapsToRemove); 
  18.      updateFlashMaps(allFlashMaps, request, response); 
  19.     } 
  20.    } 
  21.   } 
  22.   else { 
  23.    allFlashMaps.removeAll(mapsToRemove); 
  24.    updateFlashMaps(allFlashMaps, request, response); 
  25.   } 
  26.  } 
  27.  return match; 
  • 首先調用 retrieveFlashMaps 方法從 session 中獲取到所有的 FlashMap。
  • 調用 getExpiredFlashMaps 方法獲取所有過期的 FlashMap,FlashMap 默認的過期時間是 180s。
  • 獲取和當前請求匹配的 getMatchingFlashMap,具體的匹配邏輯就兩點:重定向地址要和當前請求地址相同;預設參數要相同。一般來說我們不需要配置預設參數,所以這一條可以忽略。如果想要設置,則首先給 flashMap 設置,像這樣:flashMap.addTargetRequestParam("aa", "bb");,然后在重定向的地址欄也加上這個參數:return "redirect:/orderlist?aa=bb"; 即可。
  • 將獲取到的匹配的 FlashMap 對象放入 mapsToRemove 集合中(這個匹配到的 FlashMap 即將失效,放入集合中一會被清空)。
  • 將 allFlashMaps 集合中的所有 mapsToRemove 數據清空,同時調用 updateFlashMaps 方法更新 session 中的 FlashMap。
  • 最終將匹配到的 flashMap 返回。

這就是整個獲取 flashMap 的方法,整體來看還是非常 easy 的,并沒有什么難點。

4.小結

好啦,今天就和小伙伴們分享了一下 SpringMVC 中的 flashMap,不知道大家有沒有在工作中用到這個東西?如果剛好碰到松哥前面所說的需求,用 FlashMap 真的還是蠻方便的。

本文轉載自微信公眾號「江南一點雨」,可以通過以下二維碼關注。轉載本文請聯系江南一點雨公眾號。

 

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2016-12-02 20:43:28

Android

2024-10-28 07:10:00

scroll標記前端網格布局

2023-12-29 08:17:20

接口類SpringAOP

2020-10-12 06:28:05

動態IP框架

2020-12-22 09:34:20

JavaScript開發技術

2024-09-14 09:41:17

2024-07-30 09:01:12

2022-02-14 21:58:58

netstatLinuxWindows

2019-03-28 11:07:56

Spring BootRedis緩存

2022-08-12 08:25:33

Python異常信息代碼

2015-07-27 16:13:46

Linux認證

2022-10-31 08:47:21

人臉識別按鍵鍵盤

2021-11-22 11:05:20

Vue 3setup前端

2022-10-28 19:19:11

ChromeNetwork網絡

2024-07-10 11:26:18

2020-08-27 19:30:39

Chrome瀏覽器

2015-06-08 11:21:42

iOS技巧

2020-05-09 16:45:56

ping命令Linux

2017-03-17 13:40:48

思科視頻
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕视频在线观看 | 在线观看国产视频 | 欧美成人免费在线 | 国产精品久久国产精品久久 | 日本久久久久久 | 久久成人免费视频 | 久久黄色 | 欧美激情啪啪 | 亚洲一区二区三区四区五区午夜 | 91视频在线观看免费 | 欧美日韩精品一区 | 日日摸天天添天天添破 | 亚洲精品一区中文字幕乱码 | 天天操综合网站 | 欧美电影在线观看网站 | 亚洲精品久久久久久国产精华液 | 欧美日韩精品一区二区三区四区 | 偷派自拍| 91免费在线 | 欧美一区二区三区在线播放 | 欧美一级毛片免费观看 | 精品二 | 日韩一级黄色片 | 毛片免费看 | 91视频免费观看 | 国产精品国产精品国产专区不蜜 | 久久久精 | 国产精品美女www | 欧美国产日韩一区二区三区 | 国产精品免费播放 | 一级黄色绿像片 | 黄视频免费观看 | 国产高清视频一区 | 国产精品日韩欧美一区二区三区 | 在线一区视频 | 日韩精品免费视频 | 一区二区三区久久 | 中文字幕 在线观看 | 热re99久久精品国产99热 | 伊人婷婷| 亚洲三级av |