在 SpringMVC 開發中,@ModelAttribute 和 @SessionAttribute 是兩個極具實用價值卻常被誤解的註解。前者負責在控制器方法執行前預先綁定模型數據,後者則實現了跨請求的會話級數據共享。本文將從原理、用法、源碼分析三個維度,為你徹底講透這兩個註解的核心機制。
@ModelAttribute 標註的方法會在每個目標處理方法(@RequestMapping)執行之前被調用。其本質是允許開發者在進入業務邏輯前,預先操縱模型數據,實現「預處理」邏輯的復用。
@ModelAttribute
public void populateStudent(@RequestParam(value = "id", required = false) String idStr, Map<String, Object> map) {
try {
Integer id = Integer.parseInt(idStr);
System.out.println("student id: " + id);
map.put("student", new Student(1, "李四", 23));
} catch (NumberFormatException ignored) { }
}
缺點:Spring 會額外向 Model 中放入一個鍵為 void、值為 null 的條目,污染模型數據。
@ModelAttribute("student")
public Student getStudent(@RequestParam(value = "id", required = false) String idStr) {
Student student = null;
try {
Integer id = Integer.parseInt(idStr);
System.out.println("student id: " + id);
student = new Student(1, "李四", 23);
} catch (NumberFormatException ignored) { }
return student;
}
優點:Model 中僅包含指定鍵值對("student" -> Student對象),代碼更優雅、語義更清晰。
總結:SpringMVC 在調用目標方法前,將
@ModelAttribute的value作為 key,方法返回值作為 value 存入 Model。
核心邏輯位於 HandlerMethodInvoker.invokeHandlerMethod(Spring 3.x/4.x)或 RequestMappingHandlerAdapter(Spring 5+)。關鍵步驟如下:
@ModelAttribute 的方法。@RequestParam)。value 屬性,若 Model 中已存在同名屬性則跳過(不覆蓋),否則調用方法並將返回值存入 Model。假設有兩個 @ModelAttribute 方法分別返回 student 和 student2:
@RequestMapping("/updateStudent")
public String update(Student student2) { ... }
控制台輸出:student: Student{id=23, studentName='李四', age=23}
規則:Spring 會根據參數類型(Student)在 Model 中尋找匹配的屬性,若有多個同類型屬性,使用默認命名約定(類名首字母小寫)匹配。
@RequestMapping("/updateStudent")
public String update(@ModelAttribute("student2") Student student2) { ... }
控制台輸出:student: Student{id=23, studentName='王五', age=33}
規則:強制指定從 Model 中獲取 key 為 "student2" 的屬性。
在 HandlerMethodInvoker.resolveHandlerArguments 中:
@ModelAttribute,取其 value 作為 attrName。resolveModelAttribute:優先從 implicitModel 獲取;若無且為 Session 屬性則從 Session 獲取;否則實例化新對象。if ("".equals(name)) name = Conventions.getVariableNameForParameter(methodParam); —— 未指定時自動推導默認名。@ModelAttribute 方法的 value 無直接關係,只與類型相關。@SessionAttributes(類級註解)聲明需存入 Spring MVC 會話模型的屬性名稱。標註的屬性會自動同步至 HTTP Session。
@SessionAttribute 指的是 SpringMVC 管理的會話模型。向其中添加值時,同時會寫入 HTTP Session。調用
SessionStatus.setComplete()時,會清空 SpringMVC 會話模型,並移除對應鍵的 HTTP Session 數據;但通過request.getSession().setAttribute()手動存入的數據不會被清除。其他情況下兩者行為一致。
SessionStatus 實現精確的會話清理。| 場景 | 推薦做法 |
|---|---|
| 預處理通用模型數據 | 使用 @ModelAttribute("key") + 返回值方式 |
| 目標方法需區分多個同類型模型 | 參數加 @ModelAttribute("specificKey") |
| 跨請求保持對象狀態 | 類上加 @SessionAttributes("key"),結束時 sessionStatus.setComplete() |
| 僅需臨時存放、不需跨請求 | 直接用 Model/Map,勿用 SessionAttribute |
@ModelAttribute 是「請求級預處理器」,解決模型數據準備的重複代碼問題;@SessionAttributes 是「會話級模型容器」,解決跨請求狀態保持問題。理解其執行順序、數據存取優先級、清理機制,是掌握 SpringMVC 數據流轉的關鍵。