SessionAttributes
本章聚焦 Spring MVC 跨请求保持会话数据的机制。
@SessionAttributes标注在控制器类上,将 Model 中的指定属性自动同步到HttpSession,实现跨请求的会话级数据共享。它与@SessionAttribute(参数级只读注解)形成互补,是多步骤表单、向导式流程等场景的利器。
定义与作用
@SessionAttributes 是类级别的注解,用于声明哪些 Model 属性需要自动同步到 HttpSession。当 Controller 方法向 Model 中添加指定名称的属性时,Spring MVC 会自动将其复制到 Session 中;后续请求中,即使方法不再向 Model 添加该属性,它依然可以从 Session 中恢复。
核心特征:
- 自动同步:Model → Session 的同步由框架自动完成
- 跨请求保持:属性在 Session 生命周期内对所有请求可见
- 显式清理:需要通过
SessionStatus.setComplete()标记清理,不会自动删除
生活类比:多步骤入职审批
想象飞翔科技的新员工入职流程分为三步:填写基本信息 → 上传证件 → 确认提交。每一步都是一个独立的 HTTP 请求:
- 没有
@SessionAttributes:每进入下一步,上一步填写的数据就丢失了,因为每个请求都是无状态的 - 使用
@SessionAttributes:架构师白歌在 Controller 类上标注@SessionAttributes("employeeForm"),第一步把表单数据放进 Model,Spring MVC 自动同步到 Session。第二步、第三步即使不重新查询数据库,也能从 Session 中恢复employeeForm继续处理。全部完成后,调用SessionStatus.setComplete()清理 Session 中的临时数据。
核心原理
Model → Session 同步流程
同步时机:@SessionAttributes 的同步发生在拦截器 postHandle 阶段。当 Controller 方法执行完毕、Model 数据已准备就绪后,Spring MVC 检查类上的 @SessionAttributes 声明,将匹配的属性名同步到 Session。
恢复时机:在后续请求中,如果 Controller 方法的参数类型与 Session 中存储的类型匹配,且参数名(或 @ModelAttribute 指定名)与 @SessionAttributes 声明一致,Spring MVC 会自动从 Session 中恢复该对象并注入到参数中。
适用位置与常用属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
value | String[] | 否 | 需要同步到 Session 的 Model 属性名称数组 |
types | Class<?>[] | 否 | 需要同步到 Session 的 Model 属性类型数组(按类型匹配) |
适用位置
- Controller 类:标注在类声明上
@Controller
@RequestMapping("/employee")
@SessionAttributes("employeeForm") // 将 Model 中名为 employeeForm 的属性同步到 Session
public class EmployeeWizardController {
// ...
}
也可以同时按名称和类型声明:
@SessionAttributes(value = {"employeeForm", "draftId"}, types = User.class)
完整示例
场景
飞翔科技的新员工入职采用三步向导式流程。CTO 大翔要求每一步的数据在 Session 中保持,直到最终提交。架构师白歌使用 @SessionAttributes 实现跨请求数据保持,运维李眉关注 Session 清理时机以避免内存泄漏。
向导控制器
package com.feixiang.web.controller;
import com.feixiang.web.entity.EmployeeForm;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
@Controller
@RequestMapping("/employee")
@SessionAttributes("employeeForm") // 类级别声明
public class EmployeeWizardController {
@GetMapping("/new")
public String startWizard(Model model) {
if (!model.containsAttribute("employeeForm")) {
model.addAttribute("employeeForm", new EmployeeForm());
}
return "employee/step1";
}
@PostMapping("/step1")
public String step1(@ModelAttribute("employeeForm") EmployeeForm form,
@RequestParam String name,
@RequestParam String department) {
form.setName(name);
form.setDepartment(department);
// 自动同步到 Session(由 @SessionAttributes 处理)
return "redirect:/employee/step2";
}
@PostMapping("/step2")
public String step2(@ModelAttribute("employeeForm") EmployeeForm form,
@RequestParam String idCard,
@RequestParam String phone) {
form.setIdCard(idCard);
form.setPhone(phone);
// 自动同步到 Session
return "redirect:/employee/step3";
}
@PostMapping("/submit")
public String submit(@ModelAttribute("employeeForm") EmployeeForm form,
SessionStatus status) {
// 保存到数据库
employeeService.save(form);
// 清理 Session 中的临时数据
status.setComplete();
return "employee/success";
}
}
请求示例一:第一步提交基本信息
curl -X POST http://localhost:8080/employee/step1 \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "name=张三&department=研发部" \
-c cookies.txt
行为:
- Controller 将
employeeForm放入 Model @SessionAttributes触发同步,HttpSession中存入employeeForm- 重定向到
/employee/step2
请求示例二:第二步提交证件信息(Session 自动恢复表单)
curl -X POST http://localhost:8080/employee/step2 \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "idCard=110101199001011234&phone=13800138000" \
-b cookies.txt
行为:
- Spring MVC 从 Session 中恢复
employeeForm对象注入参数 form.getName()仍然是"张三",form.getDepartment()仍然是"研发部"- 更新证件和电话后,再次同步到 Session
- 重定向到
/employee/step3
请求示例三:最终提交并清理 Session
curl -X POST http://localhost:8080/employee/submit \
-b cookies.txt
行为:
- 从 Session 恢复完整的
employeeForm(包含三步的所有数据) - 保存到数据库
status.setComplete()标记清理- 响应"提交成功"页面后,Session 中的
employeeForm被移除
易错场景与面试考点
误区一:SessionStatus.setComplete() 立即删除 Session 属性
错误认知:"调用了 setComplete(),Session 中的属性立刻就被删除了。"
纠正:setComplete() 只是标记当前 Controller 的 SessionAttributes 为"完成状态"。真正的清理发生在当前请求处理完毕后的 afterCompletion 阶段。这意味着在 setComplete() 调用后、请求结束前,你仍然可以访问 Session 中的属性。
@PostMapping("/submit")
public String submit(@ModelAttribute("employeeForm") EmployeeForm form,
SessionStatus status) {
status.setComplete(); // 标记完成
// 此时 form 仍然可用!
employeeService.save(form);
return "employee/success"; // 请求结束后才清理
}
误区二:@SessionAttributes 属性名拼写错误
错误代码:
@SessionAttributes("empForm") // 声明的是 empForm
@PostMapping("/step1")
public String step1(@ModelAttribute("employeeForm") EmployeeForm form, Model model) {
// 实际放入 Model 的是 employeeForm
model.addAttribute("employeeForm", form);
// 与 @SessionAttributes("empForm") 不匹配,不会同步到 Session!
}
问题:@SessionAttributes 按名称精确匹配。名称不一致时,Model 属性不会同步到 Session,后续请求也无法恢复。
纠正:确保 @SessionAttributes 声明的名称与 Model.addAttribute() 和 @ModelAttribute 使用的名称完全一致。
误区三:忘记清理导致 Session 膨胀
问题场景:用户填写到第二步时关闭浏览器,Session 中的 employeeForm 永远不会被清理。
应对方案:
- 务必在流程终点调用
status.setComplete() - 配置 Session 超时时间(
server.servlet.session.timeout=30m) - 对于长期未完成的向导,可在拦截器中检查时间戳,超时时主动清理
面试高频:@SessionAttributes 与 HttpSession 直接操作的区别
| 维度 | @SessionAttributes | HttpSession.setAttribute() |
|---|---|---|
| 抽象层级 | 高(与 Model 集成) | 低(直接操作 Servlet API) |
| 自动同步 | ✅ Model 自动同步到 Session | ❌ 完全手动 |
| 自动恢复 | ✅ 参数自动从 Session 恢复 | ❌ 需要手动 getAttribute |
| 清理机制 | ✅ SessionStatus.setComplete() | ❌ 需要手动 removeAttribute |
| 适用场景 | 向导式表单、多步骤流程 | 登录信息、权限等长期保持的数据 |
标准回答:@SessionAttributes 适合有明确生命周期的临时会话数据(如多步骤表单),它提供了 Model 与 Session 之间的自动同步和便捷的清理机制;而 HttpSession 直接操作适合长期保持的会话数据(如登录用户),需要开发者自行管理生命周期。
小结
@SessionAttributes 是类级别的注解,将 Model 中指定名称的属性自动同步到 HttpSession,实现跨请求的会话级数据保持。它适用于多步骤表单、向导式流程等需要跨请求共享临时数据的场景。SessionStatus.setComplete() 用于标记清理,实际删除发生在当前请求处理完毕后的 afterCompletion 阶段。
本章与全局的关系:本章讲解了向 Session 写入和保持数据的方式。它与上一章的 @SessionAttribute(只读)形成完整互补:一个负责"写和保持",一个负责"读"。下一章"RedirectAttributes"将讲解重定向场景下的临时数据传递机制。