Spring MVC 提供的這種基于注釋的編程模型猜嘱,極大的簡(jiǎn)化了 web 應(yīng)用的開(kāi)發(fā)流礁。其中 @Controller
和 @RestController
注解的組件使用 @RequestMapping
顽聂、 @ExceptionHandler
等注解來(lái)表示請(qǐng)求映射硫戈,請(qǐng)求輸入倍谜,異常處理等瓢省,使得開(kāi)發(fā)者能專注于業(yè)務(wù)邏輯的編寫窟坐,提高了開(kāi)發(fā)效率抖棘。 帶注釋的控制器具有靈活的方法簽名,不必?cái)U(kuò)展基類狸涌,也不需要實(shí)現(xiàn)特定的接口切省。
可以使用 Servlet
的 WebApplicationContext
中的標(biāo)準(zhǔn) Spring bean
定義來(lái)定義控制器 bean。 所有帶有 @Controller
注解的類會(huì)被自動(dòng)檢測(cè)帕胆,就像 Spring 通常的掃描方式一樣朝捆,檢測(cè)類路徑中的 @Component
類,并為它們自動(dòng)注冊(cè) bean 定義懒豹。 它也充當(dāng)注釋類的刻板芙盘,表示它可以作為一個(gè) Web 組件。
帶有 @RequestMapping
注解的方法叫做 Handler Method
- 處理器方法脸秽,它的參數(shù)可以來(lái)自很多地方儒老,比如 ServletRequest
、 ServletResponse
记餐、 HttpSession
等驮樊。
@ModelAttribute
在控制器的處理器方法參數(shù)上添加 @ModelAttribute
注釋可以訪問(wèn)模型中的屬性,如果不存在這個(gè)模型,則會(huì)自動(dòng)將其實(shí)例化囚衔,產(chǎn)生一個(gè)新的模型挖腰。 模型屬性還覆蓋了來(lái)自 HTTP Servlet 請(qǐng)求參數(shù)的名稱與字段名稱匹配的值,也就是請(qǐng)求參數(shù)如果和模型類中的域變量一致练湿,則會(huì)自動(dòng)將這些請(qǐng)求參數(shù)綁定到這個(gè)模型對(duì)象猴仑,這被稱為數(shù)據(jù)綁定,從而避免了解析和轉(zhuǎn)換每個(gè)請(qǐng)求參數(shù)和表單字段這樣的代碼肥哎。 例如:
@PostMapping("/componies/{componyId}/departments/{departmentId}/edit")
public String processSubmit(@ModelAttribute Department department) { }
這個(gè)處理器方法中的 department 參數(shù)會(huì)被從以下幾個(gè)來(lái)源進(jìn)行匹配綁定:
- 已經(jīng)定義過(guò)的模型方法(帶有
@ModelAttribute
的方法辽俗,后面解釋) - HTTP Session 中和字段名匹配的會(huì)話方法(帶有
@SessionAttribute
的方法,和模型方法類似篡诽,只是作用域不同) - 經(jīng)過(guò) URL 轉(zhuǎn)換器解析過(guò)的路徑變量
- 該模型類的默認(rèn)構(gòu)造方法
- 調(diào)用具有與 Servlet 請(qǐng)求參數(shù)匹配的參數(shù)的 “主構(gòu)造函數(shù)”; 參數(shù)名稱通過(guò) JavaBeans
@ConstructorProperties
或通過(guò)字節(jié)碼中的運(yùn)行時(shí)保留參數(shù)名稱確定崖飘。
雖然一般都是使用模型方法 Model method 來(lái)使用屬性填充模型,但另一種方法是依靠 Converter<String,T>
識(shí)別 URI 路徑變量來(lái)綁定霞捡。在下面的例子中,模型屬性名稱 “user” 與 URI 路徑變量 “user” 匹配薄疚,并且通過(guò)將 String 類型的用戶名交給給已注冊(cè)的 Converter<String,User>
這個(gè)轉(zhuǎn)換器來(lái)生成創(chuàng)建模型:
@PutMapping("/users/{user}")
public String saveUser(@ModelAttribute("user") User user) {
// ...
}
在獲得模型屬性實(shí)例之后碧信,請(qǐng)求數(shù)據(jù)就會(huì)被綁定到模型屬性上。 WebDataBinder
負(fù)責(zé)將 Servlet 請(qǐng)求參數(shù)名稱(查詢參數(shù)或表單字段)和目標(biāo)模型對(duì)象上的字段名稱進(jìn)行匹配街夭。 必要時(shí)會(huì)將屬性的類型進(jìn)行轉(zhuǎn)換后再填充對(duì)應(yīng)字段砰碴。
數(shù)據(jù)綁定不能保證不會(huì)出錯(cuò),發(fā)生錯(cuò)誤時(shí)默認(rèn)情況下會(huì)拋出 BindException
異常板丽,但要在處理器方法中識(shí)別出這些錯(cuò)誤呈枉,需要在 @ModelAttribute 后面添加一個(gè) BindingResult
類型的參數(shù),需要注意的是:這個(gè)參數(shù)必須和模型屬性參數(shù) (@ModelAttribute
參數(shù))相鄰埃碱,如下所示:
@PostMapping("/owners/{componyId}/departments/{departmentId}/edit")
public String processSubmit(@ModelAttribute("compony") Compony compony, BindingResult result) {
if (result.hasErrors()) {
return "componyForm";
}
// ...
}
這個(gè)例子表示如果用戶提交的表單不符合預(yù)期的匹配規(guī)則猖辫,就會(huì)返回視圖 componyForm
。
有時(shí)候我們需要獲得一個(gè)不帶數(shù)據(jù)綁定的模型屬性砚殿,也就是需要在處理器方法中使用 new
關(guān)鍵字來(lái)實(shí)例化一個(gè)對(duì)象啃憎。但是在 Spring MVC 中就不用這么麻煩了,我們可以將模型注入控制器并直接訪問(wèn)它似炎,或者可以添加 @ModelAttribute(binding = false)
來(lái)表示不需要綁定數(shù)據(jù)辛萍,如下所示:
@ModelAttribute
public UserForm setUpForm() {
return new UserForm();
}
@ModelAttribute
public User findUser(@PathVariable String userId) {
return userRepository.findOne(userId);
}
@PostMapping("update")
public String update(@Valid UserUpdateForm form, BindingResult result,
@ModelAttribute(binding=false) User user) {
// ...
}
在參數(shù)上添加 javax.validation.Valid
注解或 Spring 的 @Validated
注解,就可以在數(shù)據(jù)綁定后使用字段校驗(yàn)功能了羡藐,就像這樣:
@PostMapping("/componies/{componyId}/departments/{departmentId}/edit")
public String processSubmit(@Valid @ModelAttribute("department") Department department, BindingResult result) {
if (result.hasErrors()) {
return "departmentForm";
}
// ...
}
這樣寫和在方法體中寫 model.addAttribute("compony",compony)
是等價(jià)的贩毕。
需要注意的是 @ModelAttribute
注解如果不加,按照 BeanUtils
中的 isSimpleProperty
方法來(lái)判斷仆嗦,如果不屬于簡(jiǎn)單類型的參數(shù)辉阶,都會(huì)被自動(dòng)視為 ModelAttribute
。
歡迎訪問(wèn) 鄭保樂(lè)的博客