Spring MVC @ModelAttribute詳解

由David發表在天碼營

方法使用@ModelAttribute標註

@ModelAttribute標註可被應用在方法或方法參數上。

標註在方法上的@ModelAttribute說明方法是用於添加一個或多個屬性到model上。這樣的方法能接受與@RequestMapping標註相同的參數類型,只不過不能直接被映射到具體的請求上。

在同一個控制器中,標註了@ModelAttribute的方法實際上會在@RequestMapping方法之前被調用。

以下是示例:

// Add one attributen// The return value of the method is added to the model under the name "account"n// You can customize the name via @ModelAttribute("myAccount")nn@ModelAttributenpublic Account addAccount(@RequestParam String number) {n return accountManager.findAccount(number);n}nn// Add multiple attributesnn@ModelAttributenpublic void populateModel(@RequestParam String number, Model model) {n model.addAttribute(accountManager.findAccount(number));n // add more ...n}n

@ModelAttribute方法通常被用來填充一些公共需要的屬性或數據,比如一個下拉列表所預設的幾種狀態,或者寵物的幾種類型,或者去取得一個HTML表單渲染所需要的命令對象,比如Account等。

@ModelAttribute標註方法有兩種風格:

  • 在第一種寫法中,方法通過返回值的方式默認地將添加一個屬性;
  • 在第二種寫法中,方法接收一個Model對象,然後可以向其中添加任意數量的屬性。

可以在根據需要,在兩種風格中選擇合適的一種。

一個控制器可以擁有多個@ModelAttribute方法。同個控制器內的所有這些方法,都會在@RequestMapping方法之前被調用。

@ModelAttribute方法也可以定義在@ControllerAdvice標註的類中,並且這些@ModelAttribute可以同時對許多控制器生效。

屬性名沒有被顯式指定的時候又當如何呢?在這種情況下,框架將根據屬性的類型給予一個默認名稱。舉個例子,若方法返回一個Account類型的對象,則默認的屬性名為"account"。可以通過設置@ModelAttribute標註的值來改變默認值。當向Model中直接添加屬性時,請使用合適的重載方法addAttribute(..)-即帶或不帶屬性名的方法。

@ModelAttribute標註也可以被用在@RequestMapping方法上。這種情況下,@RequestMapping方法的返回值將會被解釋為model的一個屬性,而非一個視圖名,此時視圖名將以視圖命名約定來方式來確定。

方法參數使用@ModelAttribute標註

@ModelAttribute標註既可以被用在方法上,也可以被用在方法參數上。

標註在方法參數上的@ModelAttribute說明了該方法參數的值將由model中取得。如果model中找不到,那麼該參數會先被實例化,然後被添加到model中。在model中存在以後,請求中所有名稱匹配的參數都會填充到該參數中。

這在Spring MVC中被稱為數據綁定,一個非常有用的特性,我們不用每次都手動從表格數據中轉換這些欄位數據。

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)npublic String processSubmit(@ModelAttribute Pet pet) { }n

以上面的代碼為例,這個Pet類型的實例可能來自哪裡呢?有幾種可能:

  • 它可能因為@SessionAttributes標註的使用已經存在於model中
  • 它可能因為在同個控制器中使用了@ModelAttribute方法已經存在於model中——正如上一小節所敘述的
  • 它可能是由URI模板變數和類型轉換中取得的(下面會詳細講解)
  • 它可能是調用了自身的默認構造器被實例化出來的

@ModelAttribute方法常用於從資料庫中取一個屬性值,該值可能通過@SessionAttributes標註在請求中間傳遞。在一些情況下,使用URI模板變數和類型轉換的方式來取得一個屬性是更方便的方式。這裡有個例子:

@RequestMapping(path = "/accounts/{account}", method = RequestMethod.PUT)npublic String save(@ModelAttribute("account") Account account) {nn}n

這個例子中,model屬性的名稱("account")與URI模板變數的名稱相匹配。如果配置了一個可以將String類型的賬戶值轉換成Account類型實例的轉換器Converter<String, Account>,那麼上面這段代碼就可以工作的很好,而不需要再額外寫一個@ModelAttribute方法。

下一步就是數據的綁定。WebDataBinder類能將請求參數——包括字元串的查詢參數和表單欄位等——通過名稱匹配到model的屬性上。成功匹配的欄位在需要的時候會進行一次類型轉換(從String類型到目標欄位的類型),然後被填充到model對應的屬性中。

進行了數據綁定後,則可能會出現一些錯誤,比如沒有提供必須的欄位、類型轉換過程的錯誤等。若想檢查這些錯誤,可以在標註了@ModelAttribute的參數緊跟著聲明一個BindingResult參數:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)npublic String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {n if (result.hasErrors()) {n return "petForm";n }nn // ...nn}n

拿到BindingResult參數後,可以檢查是否有錯誤,可以通過Spring的<errors>表單標籤來在同一個表單上顯示錯誤信息。

BindingResult被用於記錄數據綁定過程的錯誤,因此除了數據綁定外,還可以把該對象傳給自己定製的驗證器來調用驗證。這使得數據綁定過程和驗證過程出現的錯誤可以被搜集到一起,然後一併返回給用戶:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)npublic String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {nn new PetValidator().validate(pet, result);n if (result.hasErrors()) {n return "petForm";n }nn // ...nn}n

又或者可以通過添加一個JSR-303規範的@Valid標註,這樣驗證器會自動被調用。

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)npublic String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {nn if (result.hasErrors()) {n return "petForm";n }nn // ...nn}n

進一步閱讀

Spring MVC的入門實例。

更深入地學習Spring MVC,請大家參考Spring MVC實戰入門訓練。

歡迎關注天碼營微信公眾號: TMY-EDU

小編重點推薦:

Spring MVC實戰入門訓練

Spring Data JPA實戰入門訓練

Java Web實戰訓練

Node.js全棧開發

更多精彩內容請訪問天碼營網站


推薦閱讀:

webservice,註解mybatis問題?
SpringMVC和Spring是什麼關係?
基於MyBatis與Spring MVC開發類知乎的簡易問答網站
Spring MVC異常處理

TAG:Spring | SpringMVC框架 |