Spring MVC 中实现 request 域效果的几种方式
request 域的本质是"一次请求内共享数据"。在 Spring MVC 中,除了直接用 HttpServletRequest.setAttribute(),还有多种封装方式,底层最终都会把数据放入 request 域。下面按使用方式分类说明。
一、原生 HttpServletRequest
最直接的方式,Controller 方法声明 HttpServletRequest 参数,手动 setAttribute。
java
@GetMapping("/demo")
public String demo(HttpServletRequest request) {
request.setAttribute("msg", "操作成功");
return "demo";
}
JSP 或 Thymeleaf 中通过 ${msg} 读取。这种方式最底层,不依赖 Spring 的封装,但代码略显繁琐。
二、Model 接口
Model 是 Spring MVC 提供的模型接口,addAttribute() 添加的数据会自动放入 request 域。
java
@GetMapping("/demo")
public String demo(Model model) {
model.addAttribute("msg", "操作成功");
model.addAttribute("user", new User("张三"));
return "demo";
}
Model 是一个接口,实际实现是 ExtendedModelMap,底层继承 ModelMap。
三、ModelMap
ModelMap 是 Model 的实现类,用法与 Model 基本一致,但它是具体类,提供了一些额外方法。
java
@GetMapping("/demo")
public String demo(ModelMap modelMap) {
modelMap.addAttribute("msg", "操作成功");
modelMap.put("user", new User("张三"));
return "demo";
}
ModelMap 继承 LinkedHashMap<String, Object>,所以也可以用 put() 方法。日常开发中 Model 和 ModelMap 可以互换,推荐用 Model,面向接口。
四、ModelAndView
ModelAndView 同时封装了模型数据和视图名称,作为方法的返回值。
java
@GetMapping("/demo")
public ModelAndView demo() {
ModelAndView mav = new ModelAndView();
mav.setViewName("demo");
mav.addObject("msg", "操作成功");
mav.addObject("user", new User("张三"));
return mav;
}
addObject() 添加的数据最终也会放入 request 域。ModelAndView 适合需要同时控制视图和数据的场景,但现代 Spring MVC 更推荐直接返回视图名 + Model 参数,代码更简洁。
五、直接使用 Map 参数
Spring MVC 支持在 Controller 方法中声明 Map<String, Object> 参数,添加到 Map 中的数据会被当作模型数据放入 request 域。
java
@GetMapping("/demo")
public String demo(Map<String, Object> map) {
map.put("msg", "操作成功");
map.put("user", new User("张三"));
return "demo";
}
底层 Spring 会把这个 Map 包装成 Model 使用。这种方式写起来简洁,但语义不如 Model 明确,团队中如果约定用 Model,就不建议混用。
六、@ModelAttribute 注解
@ModelAttribute 有两种用法,都会把数据放入 request 域。
1. 标注在方法参数上
方法参数对象会被自动放入 Model,key 默认是类型名首字母小写。
java
@GetMapping("/demo")
public String demo(@ModelAttribute("user") User user) {
// user 对象自动放入 request 域,key 为 "user"
return "demo";
}
2. 标注在方法上
被 @ModelAttribute 标注的方法会在每个 Controller 方法执行前调用,返回值自动放入 Model,常用于准备公共数据。
java
@ModelAttribute("categories")
public List<Category> categories() {
return categoryService.listAll();
}
@GetMapping("/demo")
public String demo() {
// 可以直接在视图中使用 ${categories}
return "demo";
}
这种方式适合全局共享的模型数据,比如下拉框选项、菜单数据。
七、RedirectAttributes(重定向场景)
RedirectAttributes 专门用于重定向时传递数据,它有两种添加方式,行为不同。
java
@PostMapping("/save")
public String save(User user, RedirectAttributes redirectAttributes) {
userService.save(user);
redirectAttributes.addAttribute("id", user.getId()); // 拼到 URL 上
redirectAttributes.addFlashAttribute("msg", "保存成功"); // 暂存后取出
return "redirect:/detail";
}
addAttribute():数据拼接到重定向 URL 的查询参数中,本质不是 request 域,而是 URL 参数。addFlashAttribute():数据暂存到 session(FlashMap),重定向后的下一次请求取出后立即删除,效果类似"跨请求的一次性 request 域"。
严格来说,RedirectAttributes 不是 request 域,但它是重定向场景下最常用的传参方式,需要和 request 域区分开。
八、@RequestAttribute(读取 request 域)
前面几种都是"写入"request 域。如果需要"读取"request 域中的数据,可以用 @RequestAttribute。
java
@GetMapping("/target")
public String target(@RequestAttribute("msg") String msg) {
return "target";
}
通常配合请求转发使用:前一个请求 setAttribute,转发到目标方法,目标方法用 @RequestAttribute 读取。
九、各方式对比
| 方式 | 写入 request 域 | 返回值类型 | 适用场景 |
|---|---|---|---|
HttpServletRequest |
✅ | 视图名 | 需要操作原生 Servlet API |
Model |
✅ | 视图名 | 最常用,推荐 |
ModelMap |
✅ | 视图名 | 需要 Map 风格 API |
ModelAndView |
✅ | ModelAndView |
同时控制视图和数据 |
Map 参数 |
✅ | 视图名 | 简洁写法,语义略弱 |
@ModelAttribute 参数 |
✅ | 视图名 | 表单对象绑定并放入模型 |
@ModelAttribute 方法 |
✅ | 任意 | 全局公共模型数据 |
RedirectAttributes |
重定向场景 | 重定向路径 | 重定向传参,非严格 request 域 |
十、底层关系
Model、ModelMap、ModelAndView、Map 参数、@ModelAttribute,最终都会汇聚到 ModelAndViewContainer 的 Model 中。视图渲染时,Spring 会把 Model 中的数据遍历放入 HttpServletRequest 的 attributes 中,然后交给视图解析器渲染。
所以它们的效果等价:视图里都能用 ${key} 取到值,请求结束后数据随 request 一起销毁。
区别只在于写法、返回值和适用场景,运行时没有本质差异。
十一、怎么选
| 场景 | 推荐方式 |
|---|---|
| 普通 Controller 返回视图 + 数据 | Model |
| 需要同时控制视图名和数据 | ModelAndView |
| 表单对象绑定并回显 | @ModelAttribute 参数 |
| 多个方法共享的公共数据 | @ModelAttribute 方法 |
| 需要操作原生 Servlet API | HttpServletRequest |
| 重定向后传递一次性提示 | RedirectAttributes.addFlashAttribute() |
| 读取转发过来的 request 域数据 | @RequestAttribute |
日常开发中,Model 是最常用、最推荐的方式。ModelAndView 适合老项目或需要显式控制视图的场景。@ModelAttribute 方法适合准备全局数据。RedirectAttributes 只在重定向场景使用,且要注意它和 request 域的区别。