Spring 註解面面通 之 @ModelAttribute 模型屬性繫結全解
技術標籤:Spring 全面解析SpringModelAttribute註解示例
@ModelAttribute
用於將方法引數或返回值繫結到Model屬性上,並公開給Web檢視。支援使用@RequestMapping
註釋的Controller
類。
註解解析
① value
:
待繫結到Model屬性的鍵名稱。
預設Model屬性鍵名稱根據非限定類名從宣告的屬性型別(即方法引數型別或方法返回型別)推斷而來:
mypackage.OrderAddress
型別對應的鍵名稱是orderAddress
。
List<mypackage.OrderAddress>
orderAddressList
。
② name
:
繫結的引數名稱,引數值為String
型別。name
和value
可以同時使用,但兩者的值需一致,否則會出現錯誤。
attribute 'name' and its alias 'value' are present with values of [XXX] and [XXX], but only one is permitted
③ binding
:
允許直接在@ModelAttribute
註釋的方法引數或@ModelAttribute
註釋的方法返回值上宣告禁用資料繫結,這兩種方法都將阻止資料繫結。
預設情況下,binding
true
,此時將正常進行資料繫結。將binding
設定為false
,將禁用資料繫結。
註解應用
1) @ModelAttribute
註釋方法。
@ModelAttribute
註釋方法將在Controller
中@RequestMapping
註釋方法前被呼叫。對於@ModelAttribute
註釋方法有以下幾種情況:
① @ModelAttribute
註釋void返回型別方法。
此種情況下,由於方法返回值為void型別,並不會做其他處理。因此若需要向ModeMap
中新增屬性,需要通過方法引數ModelMap
來完成。
@ModelAttribute
public void modelAttributeWithVoidMethod(ModelMap modelMap) {
modelMap.addAttribute("modelAttributeWithVoidMethod",
"@ModelAttribute註釋在void返回型別的方法上.");
}
② @ModelAttribute
註釋非void返回型別方法。
此種情況下,①
中所使用操作ModelMap
方式仍然有效,需要通過方法引數ModelMap
來新增屬性。
同時,以返回值型別推斷出的鍵、返回值作為鍵值對新增到ModelMap
中。
@ModelAttribute
public String modelAttributeWithStringMethod() {
return "@ModelAttribute註釋String返回型別方法,會自動將返回值新增到ModelMap中,鍵根據返回型別生成.";
}
③ @ModelAttribute
註釋非void返回型別方法,並指定其name
或value
屬性。
此種情況下,①
中所使用操作ModelMap
方式仍然有效,需要通過方法引數ModelMap
來新增屬性。
同時,以@ModelAttribute
註解name
或value
屬性值、返回值作為鍵值對新增到ModelMap
中。
@ModelAttribute("defModelAttributeName")
public String modelAttributeWithStringMethodDefName(ModelMap modelMap) {
return "@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.";
}
④ @ModelAttribute
與@RequestMapping
註釋同一方法。
此種情況下,@ModelAttribute
與@RequestMapping
相互作用,會使@RequestMapping
表現出稍許差異。@ModelAttribute
會使得@RequestMapping
註釋方法的某些型別返回值不會使用對應HandlerMethodReturnValueHandler
,而是由ModelAttributeMethodProcessor
解析。
ModelAttributeMethodProcessor
會針對返回值與②
或③
中進行同樣處理。檢視名稱由RequestToViewNameTranslator
根據請求/ModelAttributeWithRequestMapping.do
轉換為邏輯檢視ModelAttributeWithRequestMapping
。
不受影響的返回型別包括:ModelAndView
、 Model
、 View
、ResponseBodyEmitter
、StreamingResponseBody
、HttpEntity
、HttpHeaders
、Callable
、DeferredResult
、AsyncTask
。
@ModelAttribute
@RequestMapping(
value = "/ModelAttributeWithRequestMapping.do",
method = RequestMethod.GET)
public String modelAttributeWithRequestMapping(ModelMap modelMap) throws Exception {
logger.info("@ModelAttribute與@RequestMapping共同作用在一個方法.");
return "webannotations/ModelAttribute";
}
2) @ModelAttribute
註釋引數。
① @ModelAttribute
註釋方法引數。
此種情況下,@ModelAttribute
註解用於ModelMap
資料對映到控制器處理方法的引數中。
@RequestMapping(
value = "/ModelAttributeParameters.do",
method = RequestMethod.GET)
public ModelAndView modelAttributeParameters(@ModelAttribute("defModelAttributeName") String modelAttr,
ModelMap modelMap) throws Exception {
logger.info("@ModelAttribute註釋方法引數,從ModelMap中取值:[key=defModelAttributeName, value=" + modelAttr + "].");
return new ModelAndView("webannotations/ModelAttribute", modelMap);
}
註解示例
1) 建Controller
,用來演示@ModelAttribute
使用方法。
package com.arhorchin.securitit.webannotations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* @author Securitit.
* @note 演示@ModelAttribute使用方法.
*/
@Controller
@RequestMapping("/WebAnnotations")
public class ModelAttributeController {
/**
* logger.
*/
private Logger logger = LoggerFactory.getLogger(ModelAttributeController.class);
/**
* @ModelAttribute註釋
* void型別返回值方法.
* 需要手動ModelMap中新增資料.
*/
@ModelAttribute
public void modelAttributeWithVoidMethod(ModelMap modelMap) {
modelMap.addAttribute("modelAttributeWithVoidMethod",
"@ModelAttribute註釋在void返回型別的方法上.");
}
/**
* @ModelAttribute註釋
* String返回型別方法.
* 會自動將返回值新增到ModelMap中,鍵根據返回型別生成.
*/
@ModelAttribute
public String modelAttributeWithStringMethod() {
return "@ModelAttribute註釋String返回型別方法,會自動將返回值新增到ModelMap中,鍵根據返回型別生成.";
}
/**
* @ModelAttribute註釋
* String型別返回值方法.
* 會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.
*/
@ModelAttribute("defModelAttributeName")
public String modelAttributeWithStringMethodDefName() {
return "@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.";
}
/**
* ModelAttribute.do.
*/
@RequestMapping(
value = "/ModelAttribute.do",
method = RequestMethod.GET)
public ModelAndView modelAttribute(ModelMap modelMap) throws Exception {
logger.info("@ModelAttribute註解測試.");
return new ModelAndView("webannotations/ModelAttribute", modelMap);
}
/**
* ModelAttribute.do.
* @ModelAttribute與@RequestMapping共同註釋同一方法測試.
*/
@ModelAttribute
@RequestMapping(
value = "/ModelAttributeWithRequestMapping.do",
method = RequestMethod.GET)
public String modelAttributeWithRequestMapping() throws Exception {
logger.info("@ModelAttribute與@RequestMapping共同作用在一個方法.");
return "webannotations/ModelAttribute";
}
/**
* ModelAttributeParameters.do.
* @ModelAttribute註釋引數,可以從ModelMap中取指定引數值.
*/
@RequestMapping(
value = "/ModelAttributeParameters.do",
method = RequestMethod.GET)
public ModelAndView modelAttributeParameters(@ModelAttribute("defModelAttributeName") String modelAttr,
ModelMap modelMap) throws Exception {
logger.info("@ModelAttribute註釋方法引數,從ModelMap中取值:[key=defModelAttributeName, value=" + modelAttr + "].");
return new ModelAndView("webannotations/ModelAttribute", modelMap);
}
}
2) 建ModelAttributeHandlerInterceptor
,用來列印演示ModelMap
的值。
package com.arhorchin.securitit.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
/**
* @author Securitit.
* @note @ModelAttribute檢視ModelMap測試.
*/
public class ModelAttributeHandlerInterceptor implements HandlerInterceptor {
/**
* logger.
*/
private Logger logger = LoggerFactory.getLogger(ModelAttributeHandlerInterceptor.class);
/**
* 在HandlerAdapter實際呼叫處理程式之後呼叫,但在DispatcherServlet呈現檢視之前呼叫.
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
logger.info("==============================ModelAttributeHandlerInterceptor postHandle==============================\n"
+ JSON.toJSONString(modelAndView.getModelMap(), true));
}
}
3) 啟動服務,訪問http://localhost:9199/spring-annotations/WebAnnotations/xxxxx.do
,用來檢視@ModelAttribute
各種使用方法。
① 訪問http://localhost:9199/spring-annotations/WebAnnotations/ModelAttribute.do
,演示@ModelAttribute
註釋單獨方法。
控制檯輸出:
2020-12-16 16:04:26 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
"defModelAttributeName":"@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.",
"string":"@ModelAttribute註釋String返回型別方法,會自動將返回值新增到ModelMap中,鍵根據返回型別生成.",
"modelAttributeWithVoidMethod":"@ModelAttribute註釋在void返回型別的方法上."
}
可以看到,@ModelAttribute
註釋的modelAttributeWithVoidMethod(...)
、modelAttributeWithStringMethod()
、modelAttributeWithStringMethodDefName()
,按照解析的語義已經執行。
② 訪問http://localhost:9199/spring-annotations/WebAnnotations/ModelAttributeWithRequestMapping.do
,演示@ModelAttribute
與@RequestMapping
共同註釋一個方法。
控制檯輸出:
2020-12-16 16:11:37 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
"defModelAttributeName":"@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.",
"modelAttributeWithVoidMethod":"@ModelAttribute註釋在void返回型別的方法上.",
"string":"webannotations/ModelAttribute"
}
瀏覽器響應:
除了①
中所示語義外,還改變了@RequestMapping
註釋方法返回值的語義,將返回值按照規則新增到ModelMap
中,檢視則是由/ModelAttributeWithRequestMapping.do
來確定的。
③ 訪問http://localhost:9199/spring-annotations/WebAnnotations/ModelAttributeParameters.do
,演示@ModelAttribute
註釋方法引數。
2020-12-16 16:31:23 INFO [c.a.s.i.ModelAttributeHandlerInterceptor] ==============================ModelAttributeHandlerInterceptor postHandle==============================
{
"defModelAttributeName":"@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.",
"modelAttributeWithVoidMethod":"@ModelAttribute註釋在void返回型別的方法上.",
"string":"@ModelAttribute註釋String返回型別方法,會自動將返回值新增到ModelMap中,鍵根據返回型別生成."
}
2020-12-16 16:31:41 INFO [c.a.s.w.ModelAttributeController] @ModelAttribute註釋方法引數,從ModelMap中取值:[key=defModelAttributeName, value=@ModelAttribute註釋String型別返回值方法,會自動將返回值新增到ModelMap中,鍵是@ModelAttribute的name或value屬性值.].
可以看到,@ModelAttribute
註釋方法將從ModelMap
中獲取值,繫結到方法引數上。
總結
@ModelAttribute
主要針對ModelMap
進行操作,對於傳統的、前後端未分離的應用來說,用處還是很大的。
原始碼解析基於spring-framework-5.0.5.RELEASE
版本原始碼。
若文中存在錯誤和不足,歡迎指正!