我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringMVC中参数的传递方法有哪些

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringMVC中参数的传递方法有哪些

这篇文章主要介绍“SpringMVC中参数的传递方法有哪些”,在日常操作中,相信很多人在SpringMVC中参数的传递方法有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringMVC中参数的传递方法有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1.缘起

说到 Web 请求参数传递,大家能想到哪些参数传递方式?

参数可以放在地址栏中,不过地址栏参数的长度有限制,并且在有的场景下我们可能不希望参数暴漏在地址栏中。参数可以放在请求体中,这个没啥好说的。

小伙伴们试想这样一个场景:

在一个电商项目中,有一个提交订单的请求,这个请求是一个 POST  请求,请求参数都在请求体中。当用户提交成功后,为了防止用户刷新浏览器页面造成订单请求重复提交,我们一般会将用户重定向到一个显示订单的页面,这样即使用户刷新页面,也不会造成订单请求重复提交。

大概的代码就像下面这样:

@Controller public class OrderController {     @PostMapping("/order")     public String order(OrderInfo orderInfo) {         //其他处理逻辑         return "redirect:/orderlist";     } }

这段代码我相信大家都懂吧!如果不懂可以看看松哥录制的免费的 SpringMVC 入门教程(硬核!松哥又整了一套免费视频,搞起!)。

但是这里有一个问题:如果我想传递参数怎么办?

如果是服务器端跳转,我们可以将参数放在 request  对象中,跳转完成后还能拿到参数,但是如果是客户端跳转我们就只能将参数放在地址栏中了,像上面这个方法的返回值我们可以写成:return  "redirect:/orderlist?xxx=xxx";,这种传参方式有两个缺陷:

  • 地址栏的长度是有限的,也就意味着能够放在地址栏中的参数是有限的。

  • 不想将一些特殊的参数放在地址栏中。

那该怎么办?还有办法传递参数吗?

有!这就是今天松哥要和大家介绍的 flashMap,专门用来解决重定向时参数的传递问题。

2.flashMap

在重定向时,如果需要传递参数,但是又不想放在地址栏中,我们就可以通过 flashMap 来传递参数,松哥先来一个简单的例子大家看看效果:

首先我们定义一个简单的页面,里边就一个 post 请求提交按钮,如下:

<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>Title</title> </head> <body> <form action="/order">     <input type="submit" value="提交"> </form> </body> </html>

然后在服务端接收该请求,并完成重定向:

@Controller public class OrderController {     @PostMapping("/order")     public String order(HttpServletRequest req) {         FlashMap flashMap = (FlashMap) req.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE);         flashMap.put("name", "江南一点雨");         return "redirect:/orderlist";     }      @GetMapping("/orderlist")     @ResponseBody     public String orderList(Model model) {         return (String) model.getAttribute("name");     } }

首先在 order 接口中,获取到 flashMap 属性,然后存入需要传递的参数,这些参数最终会被 SpringMVC 自动放入重定向接口的 Model  中,这样我们在 orderlist 接口中,就可以获取到该属性了。

当然,这是一个比较粗糙的写法,我们还可以通过 RedirectAttributes 来简化这一步骤:

@Controller public class OrderController {     @PostMapping("/order")     public String order(RedirectAttributes attr) {         attr.addFlashAttribute("site", "www.javaboy.org");         attr.addAttribute("name", "微信公众号:江南一点雨");         return "redirect:/orderlist";     }      @GetMapping("/orderlist")     @ResponseBody     public String orderList(Model model) {         return (String) model.getAttribute("site");     } }

RedirectAttributes 中有两种添加参数的方式:

  • addFlashAttribute:将参数放到 flashMap 中。

  • addAttribute:将参数放到 URL 地址中。

经过前面的讲解,现在小伙伴们应该大致明白了 flashMap 的作用了,就是在你进行重定向的时候,不通过地址栏传递参数。

很多小伙伴可能会有疑问,重定向其实就是浏览器发起了一个新的请求,这新的请求怎么就获取到上一个请求保存的参数呢?这我们就要来看看 SpringMVC  的源码了。

3.源码分析

首先这里涉及到一个关键类叫做 FlashMapManager,如下:

public interface FlashMapManager {  @Nullable  FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);  void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response); }

两个方法含义一眼就能看出来:

retrieveAndUpdate:这个方法用来恢复参数,并将恢复过的的参数和超时的参数从保存介质中删除。

saveOutputFlashMap:将参数保存保存起来。

FlashMapManager 的实现类如下:

SpringMVC中参数的传递方法有哪些

从这个继承类中,我们基本上就能确定默认的保存介质时 session。具体的保存逻辑则是在 AbstractFlashMapManager 类中。

整个参数传递的过程可以分为三大步:

第一步,首先我们将参数设置到 outputFlashMap 中,有两种设置方式:我们前面的代码  req.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE) 就是直接获取  outputFlashMap 对象然后把参数放进去;第二种方式就是通过在接口中添加 RedirectAttributes 参数,然后把需要传递的参数放入  RedirectAttributes 中,这样当处理器处理完毕后,会自动将其设置到 outputFlashMap 中,具体逻辑在  RequestMappingHandlerAdapter#getModelAndView 方法中:

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

可以看到,如果 model 是 RedirectAttributes 的实例的话,则通过 getOutputFlashMap 方法获取到  outputFlashMap 属性,然后相关的属性设置进去。

这是第一步,就是将需要传递的参数,先保存到 flashMap 中。

第二步,重定向对应的视图是 RedirectView,在它的 renderMergedOutputModel 方法中,会调用  FlashMapManager 的 saveOutputFlashMap 方法,将 outputFlashMap 保存到 session 中,如下:

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,   HttpServletResponse response) throws IOException {  String targetUrl = createTargetUrl(model, request);  targetUrl = updateTargetUrl(targetUrl, model, request, response);  // Save flash attributes  RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);  // Redirect  sendRedirect(request, response, targetUrl, this.http10Compatible); }

RequestContextUtils.saveOutputFlashMap 方法最终就会调用到 FlashMapManager 的  saveOutputFlashMap 方法,将 outputFlashMap 保存下来。我们来大概看一下保存逻辑:

public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {  if (CollectionUtils.isEmpty(flashMap)) {   return;  }  String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);  flashMap.setTargetRequestPath(path);  flashMap.startExpirationPeriod(getFlashMapTimeout());  Object mutex = getFlashMapsMutex(request);  if (mutex != null) {   synchronized (mutex) {    List<FlashMap> allFlashMaps = retrieveFlashMaps(request);    allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<>());    allFlashMaps.add(flashMap);    updateFlashMaps(allFlashMaps, request, response);   }  }  else {   List<FlashMap> allFlashMaps = retrieveFlashMaps(request);   allFlashMaps = (allFlashMaps != null ? allFlashMaps : new ArrayList<>(1));   allFlashMaps.add(flashMap);   updateFlashMaps(allFlashMaps, request, response);  } }

其实这里的逻辑也很简单,保存之前会给 flashMap 设置两个属性,一个是重定向的 url 地址,另一个则是过期时间,过期时间默认 180  秒,这两个属性在第三步加载 flashMap 的时候会用到。然后将 flashMap 放入集合中,并调用 updateFlashMaps 方法存入  session 中。

第三步,当重定向请求到达 DispatcherServlet#doService 方法后,此时会调用  FlashMapManager#retrieveAndUpdate 方法从 Session 中获取 outputFlashMap 并设置到 Request  属性中备用(最终会被转化到 Model 中的属性),相关代码如下:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  //省略...  if (this.flashMapManager != null) {   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);   if (inputFlashMap != null) {    request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));   }   request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());   request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);  }  //省略... }

注意这里获取出来的 outputFlashMap 换了一个名字,变成了 inputFlashMap,其实是同一个东西。

我们可以大概看一下获取的逻辑 AbstractFlashMapManager#retrieveAndUpdate:

public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {  List<FlashMap> allFlashMaps = retrieveFlashMaps(request);  if (CollectionUtils.isEmpty(allFlashMaps)) {   return null;  }  List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps);  FlashMap match = getMatchingFlashMap(allFlashMaps, request);  if (match != null) {   mapsToRemove.add(match);  }  if (!mapsToRemove.isEmpty()) {   Object mutex = getFlashMapsMutex(request);   if (mutex != null) {    synchronized (mutex) {     allFlashMaps = retrieveFlashMaps(request);     if (allFlashMaps != null) {      allFlashMaps.removeAll(mapsToRemove);      updateFlashMaps(allFlashMaps, request, response);     }    }   }   else {    allFlashMaps.removeAll(mapsToRemove);    updateFlashMaps(allFlashMaps, request, response);   }  }  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 的,并没有什么难点。

到此,关于“SpringMVC中参数的传递方法有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringMVC中参数的传递方法有哪些

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java传递参数的方法有哪些

在Java中,传递参数的方法有以下几种:1. 值传递:基本数据类型(如int、char、boolean等)和字符串类型(String)都是以值传递的方式进行参数传递。在方法内对参数的修改不会影响原始值。2. 引用传递:对于引用类型(如数组、
2023-09-15

Python中函数参数传递方法有哪些

这篇文章主要介绍“Python中函数参数传递方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python中函数参数传递方法有哪些”文章能帮助大家解决问题。定义和传递参数parameters
2023-07-06

shell中脚本参数传递的方法有哪些

这篇文章主要介绍了shell中脚本参数传递的方法有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。方式一:$0,$1,$2..采用$0,$1,$2..等方式获取脚本命令行传
2023-06-09

python函数参数传递的方法有哪些

在Python中,函数参数传递的方法有以下几种:1. 位置参数传递:根据参数的位置顺序进行传递,参数的值与参数的位置一一对应。例如:pythondef func(a, b, c):# 函数体func(1, 2, 3)2. 关键字参数传递:根
2023-10-19

python传递参数的方式有哪些

在Python中,有以下几种方式可以传递参数:1. 位置参数:按照参数顺序传递,参数的位置和数量必须与函数定义中的参数一致。```pythondef func(a, b, c):# 函数体func(1, 2, 3)```2. 默认参数:在函
2023-09-27

java参数传递的方式有哪些

在Java中,参数传递的方式有以下几种:值传递:基本类型和不可变对象通过值传递方式进行参数传递。在值传递中,传递的是实际值的副本,对副本的修改不会影响原始值。引用传递:可变对象通过引用传递方式进行参数传递。在引用传递中,传递的是对象的引用,
java参数传递的方式有哪些
2024-02-29

c#参数传递的方式有哪些

在C#中,参数传递的方式有以下几种:1. 值传递(By Value):将实参的值复制给形参,即在函数内部对形参的修改不会影响实参的值。2. 引用传递(By Reference):将实参的引用传递给形参,即形参和实参指向同一个内存地址,对形参
2023-10-18

React传递参数的方式有哪些

本篇内容介绍了“React传递参数的方式有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!父子组件之间传递参数父组件往子组件传值,直接用t
2023-06-20

python中函数传递参数的方式有哪些

本篇文章为大家展示了python中函数传递参数的方式有哪些,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几乎无所不
2023-06-14

C++ 中函数参数的传递方式有哪些?

c++++ 中,传递给函数的参数可通过三种方式传递:传值传递(创建形参副本)、传引用传递(直接访问实参)、传指针传递(访问实参的内存地址)。根据函数行为和内存管理要求选择最佳传递方式,权衡副本创建、直接修改和内存管理之间的利弊。C++ 中函
C++ 中函数参数的传递方式有哪些?
2024-04-12

qt多线程传递参数的方法有哪些

在Qt中,有多种方法可以在多线程之间传递参数。以下是其中一些常用的方法:1. 构造函数传递参数:在创建新的线程对象时,通过构造函数将参数传递给线程对象。然后在线程内部通过成员变量或者函数参数来使用这些参数。2. 使用QThread的setP
2023-09-13

PHP 函数的参数传递方式有哪些?

php 函数参数可以通过引用或按值传递。按值传递不会修改外部变量,而引用传递会直接修改外部变量,使用 & 符号启用。引用传递常用于修改数组元素和优化性能。PHP 函数的参数传递方式在 PHP 中,函数参数可以通过引用传递或按值传递
PHP 函数的参数传递方式有哪些?
2024-04-10

C++ 函数参数传递方式有哪些?

在 c++++ 中,有四种参数传递方式:1. 传值(副本)2. 传引用 3. 传常引用 4. 传指针。传值和传引用用于修改原值,常引用用于只读访问,而传指针用于操作内存地址。C++ 函数参数传递方式在 C++ 中,将数据传递给函数的方法有
C++ 函数参数传递方式有哪些?
2024-04-12

SpringMVC中RequestBody注解的List参数传递方式

这篇文章主要介绍了SpringMVC中RequestBody注解的List参数传递方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

python可变参数的传递方式有哪些

这篇文章主要讲解了“python可变参数的传递方式有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“python可变参数的传递方式有哪些”吧!1、包裹位置传递,所有的传入参数都会被args
2023-06-20

java线程传递参数的方式有哪些

在Java中,线程传递参数的方式有以下几种:使用构造函数传递参数:可以在创建线程对象时,通过构造函数将参数传递给线程对象。通过在子类中定义带参数的构造函数,并在调用start()方法之前传递参数,可以实现线程间的参数传递。使用成员变量传递参
2023-10-27

编程热搜

目录