javascript
使用 Spring 2.5 基于注解驱动的 Spring MVC(二)
我們在 ② 處添加了一個 ModelMap 屬性,其屬性名為 currUser,而 ① 處通過 @SessionAttributes 注解將 ModelMap 中名為 currUser 的屬性放置到 Session 中,所以我們不但可以在 listBoardTopic() 請求所對應的 JSP 視圖頁面中通過 request.getAttribute(“currUser”) 和 session.getAttribute(“currUser”) 獲取 user 對象,還可以在下一個請求所對應的 JSP 視圖頁面中通過 session.getAttribute(“currUser”) 或 ModelMap#get(“currUser”) 訪問到這個屬性。
這里我們僅將一個 ModelMap 的屬性放入 Session 中,其實 @SessionAttributes 允許指定多個屬性。你可以通過字符串數組的方式指定多個屬性,如 @SessionAttributes({“attr1”,”attr2”})。此外,@SessionAttributes 還可以通過屬性類型指定要 session 化的 ModelMap 屬性,如 @SessionAttributes(types = User.class),當然也可以指定多個類,如 @SessionAttributes(types = {User.class,Dept.class}),還可以聯合使用屬性名和屬性類型指定:@SessionAttributes(types = {User.class,Dept.class},value={“attr1”,”attr2”})。
上面 講述了如何往ModelMap中放置屬性以及如何使ModelMap中的屬性擁有Session域的作用范圍。除了在JSP視圖頁面中通過傳統的方法訪問 ModelMap中的屬性外,讀者朋友可能會問:是否可以將ModelMap中的屬性綁定到請求處理方法的入參中呢?答案是肯定的。Spring為此提供 了一個@ModelAttribute的注解,下面是使用@ModelAttribute注解的例子:
清單 11. 使模型對象的特定屬性具有 Session 范圍的作用域
| package com.baobaotao.web;import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.ModelAttribute;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession;@Controller @RequestMapping("/bbtForum.do") @SessionAttributes("currUser") //①讓ModelMap的currUser屬性擁有session級作用域 public class BbtForumController {@Autowired private BbtForumService bbtForumService;@RequestMapping(params = "method=listBoardTopic")public String listBoardTopic(@RequestParam("id")int topicId, User user, ModelMap model) {bbtForumService.getBoardTopics(topicId);System.out.println("topicId:" + topicId);System.out.println("user:" + user);model.addAttribute("currUser",user); //②向ModelMap中添加一個屬性return "listTopic";}@RequestMapping(params = "method=listAllBoard")//③將ModelMap中的 public String listAllBoard(@ModelAttribute("currUser") User user) { //currUser屬性綁定到user入參中。bbtForumService.getAllBoard();System.out.println("user:"+user);return "listBoard";} } |
?
在 ② 處,我們向 ModelMap 中添加一個名為 currUser 的屬性,而 ① 外的注解使這個 currUser 屬性擁有了 session 級的作用域。所以,我們可以在 ③ 處通過 @ModelAttribute 注解將 ModelMap 中的 currUser 屬性綁定以請求處理方法的 user 入參中。
所以當我們先調用以下 URL 請求: http://localhost/bbtForum.do?method=listBoardTopic&id=1&userName=tom&dept.deptId=12
以執行listBoardTopic()請求處理方法,然后再訪問以下URL: http://localhost/sample/bbtForum.do?method=listAllBoard
你將可以看到 listAllBoard() 的 user 入參已經成功綁定到 listBoardTopic() 中注冊的 session 級的 currUser 屬性上了。
?
|
請求處理方法的簽名規約
方法入參
我們知道標注了 @RequestMapping 注解的 Controller 方法就成為了請求處理方法,Spring MVC 允許極其靈活的請求處理方法簽名方式。對于方法入參來說,它允許多種類型的入參,通過下表進行說明:
| Java 基本數據類型和 String | 默認情況下將按名稱匹配的方式綁定到 URL 參數上,可以通過 @RequestParam 注解改變默認的綁定規則 |
| request/response/session | 既可以是 Servlet API 的也可以是 Portlet API 對應的對象,Spring 會將它們綁定到 Servlet 和 Portlet 容器的相應對象上 |
| org.springframework.web.context.request.WebRequest | 內部包含了 request 對象 |
| java.util.Locale | 綁定到 request 對應的 Locale 對象上 |
| java.io.InputStream/java.io.Reader | 可以借此訪問 request 的內容 |
| java.io.OutputStream / java.io.Writer | 可以借此操作 response 的內容 |
| 任何標注了 @RequestParam 注解的入參 | 被標注 @RequestParam 注解的入參將綁定到特定的 request 參數上。 |
| java.util.Map / org.springframework.ui.ModelMap | 它綁定 Spring MVC 框架中每個請求所創建的潛在的模型對象,它們可以被 Web 視圖對象訪問(如 JSP) |
| 命令/表單對象(注:一般稱綁定使用 HTTP GET 發送的 URL 參數的對象為命令對象,而稱綁定使用 HTTP POST 發送的 URL 參數的對象為表單對象) | 它們的屬性將以名稱匹配的規則綁定到 URL 參數上,同時完成類型的轉換。而類型轉換的規則可以通過 @InitBinder 注解或通過 HandlerAdapter 的配置進行調整 |
| org.springframework.validation.Errors / org.springframework.validation.BindingResult | 為屬性列表中的命令/表單對象的校驗結果,注意檢驗結果參數必須緊跟在命令/表單對象的后面 |
| rg.springframework.web.bind.support.SessionStatus | 可以通過該類型 status 對象顯式結束表單的處理,這相當于觸發 session 清除其中的通過 @SessionAttributes 定義的屬性 |
Spring MVC 框架的易用之處在于,你可以按任意順序定義請求處理方法的入參(除了 Errors 和 BindingResult 必須緊跟在命令對象/表單參數后面以外),Spring MVC 會根據反射機制自動將對應的對象通過入參傳遞給請求處理方法。這種機制讓開發者完全可以不依賴 Servlet API 開發控制層的程序,當請求處理方法需要特定的對象時,僅僅需要在參數列表中聲明入參即可,不需要考慮如何獲取這些對象,Spring MVC 框架就象一個大管家一樣“不辭辛苦”地為我們準備好了所需的一切。下面演示一下使用 SessionStatus 的例子:
清單 12. 使用 SessionStatus 控制 Session 級別的模型屬性
| @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {//<——①new OwnerValidator().validate(owner, result);if (result.hasErrors()) {return "ownerForm";}else {this.clinic.storeOwner(owner);status.setComplete();//<——②return "redirect:owner.do?ownerId=" + owner.getId();} } |
?
processSubmit() 方法中的 owner 表單對象將綁定到 ModelMap 的“owner”屬性中,result 參數用于存放檢驗 owner 結果的對象,而 status 用于控制表單處理的狀態。在 ② 處,我們通過調用 status.setComplete() 方法,該 Controller 所有放在 session 級別的模型屬性數據將從 session 中清空。
方法返回參數
在低版本的 Spring MVC 中,請求處理方法的返回值類型都必須是 ModelAndView。而在 Spring 2.5 中,你擁有多種靈活的選擇。通過下表進行說明:
| void | 此時邏輯視圖名由請求處理方法對應的 URL 確定,如以下的方法:
對應的邏輯視圖名為“welcome” | |
| String | 此時邏輯視圖名為返回的字符,如以下的方法:
對應的邏輯視圖名為“ownerForm” | |
| org.springframework.ui.ModelMap | 和返回類型為 void 一樣,邏輯視圖名取決于對應請求的 URL,如下面的例子:
對應的邏輯視圖名為“vets”,返回的 ModelMap 將被作為請求對應的模型對象,可以在 JSP 視圖頁面中訪問到。 | |
| ModelAndView | 當然還可以是傳統的 ModelAndView。 |
應該說使用 String 作為請求處理方法的返回值類型是比較通用的方法,這樣返回的邏輯視圖名不會和請求 URL 綁定,具有很大的靈活性,而模型數據又可以通過 ModelMap 控制。當然直接使用傳統的 ModelAndView 也不失為一個好的選擇。
?
|
注冊自己的屬性編輯器
Spring MVC 有一套常用的屬性編輯器,這包括基本數據類型及其包裹類的屬性編輯器、String 屬性編輯器、JavaBean 的屬性編輯器等。但有時我們還需要向 Spring MVC 框架注冊一些自定義的屬性編輯器,如特定時間格式的屬性編輯器就是其中一例。
Spring MVC 允許向整個 Spring 框架注冊屬性編輯器,它們對所有 Controller 都有影響。當然 Spring MVC 也允許僅向某個 Controller 注冊屬性編輯器,對其它的 Controller 沒有影響。前者可以通過 AnnotationMethodHandlerAdapter 的配置做到,而后者則可以通過 @InitBinder 注解實現。
下面先看向整個 Spring MVC 框架注冊的自定義編輯器:
清單 13. 注冊框架級的自定義屬性編輯器
| >bean class="org.springframework.web.servlet.mvc.annotation. AnnotationMethodHandlerAdapter"<>property name="webBindingInitializer"<>bean class="com.baobaotao.web.MyBindingInitializer"/<>/property< >/bean< |
?
MyBindingInitializer 實現了 WebBindingInitializer 接口,在接口方法中通過 binder 注冊多個自定義的屬性編輯器,其代碼如下所示:
清單 14.自定義屬性編輯器
| package org.springframework.samples.petclinic.web;import java.text.SimpleDateFormat; import java.util.Date;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.samples.petclinic.Clinic; import org.springframework.samples.petclinic.PetType; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.context.request.WebRequest;public class MyBindingInitializer implements WebBindingInitializer {public void initBinder(WebDataBinder binder, WebRequest request) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");dateFormat.setLenient(false);binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));} } |
?
如果希望某個屬性編輯器僅作用于特定的 Controller,可以在 Controller 中定義一個標注 @InitBinder 注解的方法,可以在該方法中向 Controller 了注冊若干個屬性編輯器,來看下面的代碼:
清單 15. 注冊 Controller 級的自定義屬性編輯器
| @Controller public class MyFormController {@InitBinderpublic void initBinder(WebDataBinder binder) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");dateFormat.setLenient(false);binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));}… } |
?
注意被標注 @InitBinder 注解的方法必須擁有一個 WebDataBinder 類型的入參,以便 Spring MVC 框架將注冊屬性編輯器的 WebDataBinder 對象傳遞進來。
?
|
如何準備數據
在 編寫 Controller 時,常常需要在真正進入請求處理方法前準備一些數據,以便請求處理或視圖渲染時使用。在傳統的 SimpleFormController 里,是通過復寫其 referenceData() 方法來準備引用數據的。在 Spring 2.5 時,可以將任何一個擁有返回值的方法標注上 @ModelAttribute,使其返回值將會進入到模型對象的屬性列表中。來看下面的例子:
清單 16. 定義為處理請求準備數據的方法
| package com.baobaotao.web;import com.baobaotao.service.BbtForumService; import org.springframework.beans.factory.annotation.Autowired; 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.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes;import java.util.ArrayList; import java.util.List; import java.util.Set;@Controller @RequestMapping("/bbtForum.do") public class BbtForumController {@Autowiredprivate BbtForumService bbtForumService;@ModelAttribute("items")//<——①向模型對象中添加一個名為items的屬性public List<String> populateItems() {List<String> lists = new ArrayList<String>();lists.add("item1");lists.add("item2");return lists;}@RequestMapping(params = "method=listAllBoard")public String listAllBoard(@ModelAttribute("currUser")User user, ModelMap model) {bbtForumService.getAllBoard();//<——②在此訪問模型中的items屬性System.out.println("model.items:" + ((List<String>)model.get("items")).size());return "listBoard";} } |
?
在 ① 處,通過使用 @ModelAttribute 注解,populateItem() 方法將在任何請求處理方法執行前調用,Spring MVC 會將該方法返回值以“items”為名放入到隱含的模型對象屬性列表中。
所 以在 ② 處,我們就可以通過 ModelMap 入參訪問到 items 屬性,當執行 listAllBoard() 請求處理方法時,② 處將在控制臺打印出“model.items:2”的信息。當然我們也可以在請求的視圖中訪問到模型對象中的 items 屬性。
?
|
小結
Spring 2.5 對 Spring MVC 進行了很大增強,現在我們幾乎完全可以使用基于注解的 Spring MVC 完全替換掉原來基于接口 Spring MVC 程序。基于注解的 Spring MVC 比之于基于接口的 Spring MVC 擁有以下幾點好處:
- 方便請求和控制器的映射;
- 方便請求處理方法入參綁定URL參數;
- Controller 不必繼承任何接口,它僅是一個簡單的 POJO。
但 是基于注解的 Spring MVC 并不完美,還存在優化的空間,因為在某些配置上它比基于 XML 的配置更繁瑣。比如對于處理多個請求的 Controller 來說,假設我們使用一個 URL 參數指定調用的處理方法(如 xxx.do?method=listBoardTopic),當使用注解時,每個請求處理方法都必須使用 @RequestMapping() 注解指定對應的 URL 參數(如 @RequestMapping(params = "method=listBoardTopic")),而在 XML 配置中我們僅需要配置一個 ParameterMethodNameResolver 就可以了。
轉載于:https://www.cnblogs.com/sunwei2012/archive/2010/05/11/1732521.html
總結
以上是生活随笔為你收集整理的使用 Spring 2.5 基于注解驱动的 Spring MVC(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: A7VU坦克介绍
- 下一篇: 找一言情小说男主排老三是军人父亲是军人,