SpringMVC 的映射


27.1.1 @RequestMapping使用

之前,我們是把@RequestMapping注解放在方法之上,用來給方法綁定一個請求映射。除此以外,@RequestMapping注解還可以放在類的上面。例如,我們給之前的請求處理類(FirstSpringDemo.java)的類名上也加一個@RequestMapping注解,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping("/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/firstSpringMVC")
  6. public String welcomeToSpringMVC()
  7. {
  8. System.out.println("welcome to springMVC");
  9. return "success";
  10. }
  11. }

類前面加了@RequestMapping注解以后,前端發來的請求就不能再直接去匹配方法上面的@RequestMapping了。而是應該先匹配類前的@RequestMapping值,再匹配方法前的@RequestMapping

因此,方法前面@RequestMapping的值,是相對於類前的@RequestMapping值。如果類的前面不存在@RequestMapping,則方法前的@RequestMapping值就是相對於項目根目錄。 例如,在類前加了@ RequestMapping("/FirstSpringDemo")以后,前台就必須通過以下路徑來訪問:

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/firstSpringMVC">
  3. My First SpringMVC Demo
  4. </a>
  5. </body>

即先通過“FirstSpringDemo”匹配類前的@RequestMapping("/FirstSpringDemo") ,再通過“ firstSpringMVC”匹配方法前的@RequestMapping("/firstSpringMVC")

27.1.2 @RequestMapping屬性

@RequestMapping注解的常用屬性如下,

屬性 簡介
value 指定請求的實際URL地址,屬性名value可省。例如@RequestMapping("/firstSpringMVC")等價於@RequestMapping(value="/firstSpringMVC")
method 指定請求方式,包含 GET(默認)、POST、PUT、DELETE等;可以通過枚舉類RequestMethod設置,如method=RequestMethod.POST。
params 規定請求中的些參數值,必須滿足一定的條件。params本身是一個字符串數組。
headers 規定請求中的請求頭(header)必須滿足一定的條件。

(1)method屬性

例如,因為超鏈接本身是采用GET方式提交請求。因此,若前台仍然是通過<ahref="FirstSpringDemo/firstSpringMVC">...</a>發送請求,則處理類必須使用GET方式才能接受到此請求。如果使用“POST”等其他方式,是無法獲取到該請求的,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/firstSpringMVC"
  6. ,method=RequestMethod.POST)
  7. public String welcomeToSpringMVC()
  8. {
  9. return "success";
  10. }
  11. }

如果再點擊上面超鏈接,運行結果如下,

圖27-01

提示我們“請求方法不支持GET方式”。

如果把超鏈接,替換成以下POST方式的表單提交,

index.jsp

復制
  1. <body>
  2. <form action="FirstSpringDemo/firstSpringMVC" method="POST">
  3. <input type="submit" value="POST方式提交"/>
  4. </form>
  5. </body>

點擊“POST方式提交”后,就又會正常運行。

(2)params屬性

例如,我們通過超鏈接加入兩個請求參數,如下,

index.jsp

復制
  1. <body>
  2. <a href=
  3. "FirstSpringDemo/requestWithParams?name=zhangsan&age=20">
  4. requestWithParams...
  5. </a>
  6. </body>

再通過params來檢查請求中的參數是否符合要求,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithParams"
  6. ,params={"name","age!=23"})
  7. public String requestWithParams()
  8. {
  9. return "success";
  10. }
  11. }

以上請求通過params規定請求參數必須包含“name”參數,並且“age!=23”,我們之前發來的請求“…?name=zhangsan&age=20”符合要求,因此可以被該方法接收並處理。如果發送的請求參數是“…?name=zhangsan&age=23”或“…? age=23”等不符合params規定,就會引發我們所熟悉 “404”異常。

params支持以下表達式:

表達式 簡介
paramName 必須包含參數名為“paramName”的參數。
!paramName 不能包含參數名為“paramName”的參數。
paramName!=paramValue 必須包含參數名為“paramName”的參數,但參數值不能是“paramValue”。

(3)headers屬性

SpringMVC用headers來約束“參數”,用headers來約束“請求頭”。我們可以在火狐瀏覽器里打開“firebug”查看每一次請求的“請求頭”,如下,

圖27-02

“請求頭”指明了請求所攜帶的MIME類型、字符集等信息。

例如,可以通過“headers”指定請求頭中的“Accept-Language”必須是“zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3”,以及“Accept-Encoding”必須是“gzip, deflate”,如下

FirstSpringDemo.java

復制
  1. @RequestMapping(value="/requestWithHeaders",
  2. headers={"Accept-Language=zh-CN,zh;
  3. q=0.8,en-US;q=0.5,en;q=0.3",
  4. "Accept-Encoding=gzip, deflate"})
  5. public String requestWithHeaders()
  6. {
  7. return "success";
  8. }

關於“請求頭”的知識,讀者可以查閱相關資料,本書不作為重點講解。

27.2 基於@RequestMapping的配置

27.2.1 Ant風格的請求路徑

SpringMVC除了支持傳統方式的請求外,還支持Ant風格的請求路徑。

Ant風格的請求路徑支持以下3種通配符:

通配符 簡介
? 匹配任何單字符
* 匹配0或者任意數量的字符
** 匹配0或者更多的目錄

例如,在處理方法前配置@RequestMapping(value="/requestWithAntPath/*/test"),表示請求路徑的“requestWithAntPath”和“test”之間可以填任意字符,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithAntPath/*/test")
  6. public String requestWithAntPath()
  7. {
  8. return "success";
  9. }
  10. }

如果前端發送以下請求,是可以匹配到requestWithAntPath()方法的。

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/requestWithAntPath/lanqiao/test">
  3. requestWithAntPath...
  4. </a>
  5. </body>

其他Ant風格的示例如下表,

請求路徑 匹配的示例
/requestWithAntPath/**/test /requestWithAntPath/a/b/test、/requestWithAntPath/test等
/requestWithAntPath/test?? /requestWithAntPath/testxy、/requestWithAntPath/testqq等

27.2.2 使用@PathVariable獲取動態參數

在SpringMVC中,可以使用@PathVariable來獲得請求路徑中的動態參數。

如下,通過前端傳入一個參數“9527”,

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/requestWithPathVariable/9527">
  3. requestWithPathVariable...
  4. </a>
  5. </body>

處理方法就可以通過@PathVariable來獲取此參數值,如下

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithPathVariable/{id}")
  6. public String
  7. requestWithPathVariable(@PathVariable("id") Integer id)
  8. {
  9. System.out.println("id:"+id);
  10. return "success";
  11. }}

具體是通過@RequestMapping(value="/requestWithPathVariable/{id}")中的占位符“{id}”接收到參數值“9527”,再把參數值傳給@PathVariable("id")中的“id”,最后再把值賦值給方法的參數id

27.2.3 REST風格

REST(Representational State Transfer)是一種編程風格,可以顯著降低開發的復雜性,是當前非常流行的一種互聯網軟件架構。

在學習REST之前,我們首先要知道,在HTTP協議里面有多種請求方式,並且其中的POSTDELETEPUTGET等四個方式,分別對應增刪改查四種操作,具體是:POST對應“增”,DELETE對應“刪”、PUT對應“改”,GET對應“查”。但是普通瀏覽器中的form表單,只支持GETPOST兩種請求方式。為了使普通瀏覽器支持PUTDELETE方式,可以使用Spring提供的過濾器HiddenHttpMethodFilter,此過濾器可以通過一定的規則,將部分POST請求轉換為PUTDELETE請求。如果讀者想了解HiddenHttpMethodFilter的底層代碼,可以閱讀spring-web-x.x.xRELEASE.jar包中HiddenHttpMethodFilter類里的doFilterInternal()方法。

實現PUTDELETE請求方式的步驟如下:

1.web.xml中配置HiddenHttpMethodFilter過濾器,如下,

web.xml

復制
  1. <web-app …>
  2. <filter>
  3. <filter-name>HiddenHttpMethodFilter</filter-name>
  4. <filter-class>
  5. org.springframework.web.filter.HiddenHttpMethodFilter
  6. </filter-class>
  7. </filter>
  8. <filter-mapping>
  9. <filter-name>HiddenHttpMethodFilter</filter-name>
  10. <url-pattern>/*</url-pattern>
  11. </filter-mapping>
  12. </web-app>

2.form表單中指定請求方式為method="post";並在表單中增加一個hidden隱藏域,且設置隱藏域的namevalue屬性:name="_method"value="PUT"value="DELETE"

3.在處理方法的@RequestMapping注解中,用method屬性指定請求方式(如method=RequestMethod.DELETEmethod=RequestMethod.PUT等)。

例如,在web.xml中配置了HiddenHttpMethodFilter以后,就可以使用下面的方式發送並處理增、刪、改、查的請求:

①發送請求

index.jsp

復制
  1. <body>
  2. <form action="FirstSpringDemo/requestWithREST/9527"
  3. method="post">
  4. <input type="hidden" name="_method" value="DELETE" />
  5. <input type="submit" value="刪除" />
  6. </form>
  7. <form action="FirstSpringDemo/requestWithREST/9527"
  8. method="post">
  9. <input type="hidden" name="_method" value="PUT" />
  10. <input type="submit" value="修改" />
  11. </form>
  12. <form action="FirstSpringDemo/requestWithREST/9527"
  13. method="post">
  14. <input type="submit" value="增加" />
  15. </form>
  16. <a href="FirstSpringDemo/requestWithREST/9527">查看</a>
  17. </body>

②處理請求

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. //使用REST風格,處理“刪除”的請求
  6. @RequestMapping(value="/requestWithREST/{id}",
  7. method=RequestMethod.DELETE)
  8. public String requestWithRestDelete(@PathVariable("id")
  9. Integer id)
  10. {
  11. System.out.println("刪除時需要的id:"+id);
  12. return "success";
  13. }
  14. //使用REST風格,處理“修改”的請求
  15. @RequestMapping(value="/requestWithREST/{id}",
  16. method=RequestMethod.PUT)
  17. public String requestWithRestPut(@PathVariable("id")
  18. Integer id)
  19. {
  20. System.out.println("修改時需要的id:"+id);
  21. return "success";
  22. }
  23. //使用REST風格,處理“增加”的請求
  24. @RequestMapping(value="/requestWithREST/{id}",
  25. method=RequestMethod.POST)
  26. public String requestWithRestAdd(@PathVariable("id")
  27. Integer id)
  28. {
  29. System.out.println("增加時需要的id:"+id);
  30. return "success";
  31. }
  32. //使用REST風格,處理“查看”的請求
  33. @RequestMapping(value="/requestWithREST/{id}",
  34. method=RequestMethod.GET)
  35. public String requestWithRestGet(@PathVariable("id") Integer id)
  36. {
  37. System.out.println("查詢時需要的id:"+id);
  38. return "success";
  39. }
  40. }

運行index.jsp頁面,如圖,

圖27-03

依次點擊刪除、修改、增加、查看按鈕,可在控制台得到以下結果:

圖27-04

27.2.4 使用@RequestParam獲取請求參數

Spring MVC可以通過@RequestParam來接收請求中的參數值,該注解有三個常用的屬性:

屬性名 簡介
value 請求攜帶參數的參數名
required 標識請求參數中是否必須存在某個具體的參數。 true(默認):必須存在;若不存在,將拋出異常。 false:不必須。
defaultValue 給參數賦一個默認值。如果請求中不存在某個參數,則該參數就取defaultValue所設置的值。

index.jsp

復制
  1. <a href="FirstSpringDemo/requestParam?name=zhangsan&age=23">
  2. TestRequestParam
  3. </a>

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. //使用@RequestParam注解接收請求參數
  6. @RequestMapping("/requestParam")
  7. public String requestParam(@RequestParam(value="name") String name, @RequestParam(value="age") Integer age)
  8. {
  9. System.out.println("name: " + name + " age: " + age);
  10. return "success";
  11. }
  12. }…

@RequestParam通過value值與傳入的參數名匹配,並將參數值賦值給@RequestParam后面的變量。例如,通過@RequestParam(value="name")接收index.jsp傳來的name參數值(即zhangsan),並將參數值(zhangsan)賦值給@RequestParam后面的String name,類似於String name="zhangsan"

若將請求中的age參數刪除,如下

index.jsp

復制
  1. <a href="FirstSpringDemo/requestParam?name=zhangsan">
  2. TestRequestParam
  3. </a>

再次執行以上超鏈接,則會發生異常,如下:

圖27-05

為了解決此異常,就可以給age參數的@RequestParam加入required=false,如下:

FirstSpringDemo.java

復制
  1. @RequestMapping("/requestParam")
  2. public String requestParam(@RequestParam(value = "name") String name, @RequestParam(value = "age",required=false) Integer age)
  3. {
  4. }

此外,還可以通過@RequestParamdefaultValue屬性給請求參數設置默認值,如下,

FirstSpringDemo.java

復制
  1. @RequestMapping("/requestParam")
  2. public String requestParam(@RequestParam(value = "name")
  3. String name, @RequestParam(value = "age",
  4. required=false,defaultValue="23") Integer age)
  5. {
  6. System.out.println("name: " + name + " age: " + age);
  7. return "success";
  8. }…

通過defaultValue="23"age的默認值設置為”23”,即如果前端發送的請求中沒有攜帶age參數,則age的值就是23。

27.2.5 @RequestHeader注解

在HTTP協議中,每一次請求都會攜帶相關的“頭信息”,例如可以在fireBug中觀察到以下頭信息:

圖27-06

SpringMVC也提供了@RequestHeader注解來幫助我們獲取請求中的“頭信息”,如下:

index.jsp

<a href="FirstSpringDemo/requestHeader"> requestHeader</a><br/>

FirstSpringDemo

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/requestHeader")
  6. public String
  7. requestHeader(@RequestHeader(value="Accept-Language")
  8. String al){
  9. System.out.println("Accept-Language:" + al);
  10. return "success";
  11. }
  12. }

通過@RequestHeader獲取“頭信息”,並通過value屬性指定獲取頭信息中的Accept-Language值,並把值賦值給al參數。

執行index.jsp中的requestHeader超鏈接,可在控制台得到以下結果:

圖27-07

27.2.6 @CookieValue注解

@CookieValue可以給處理方法入參綁定某個Cookie值。例如,客戶端有一個名為JSESSIONID的Cookie對象,服務端可以通過@CookieValue來獲取此JSESSIONID的值:

index.jsp

<a href="FirstSpringDemo/cookieValue">cookieValue</a><br/>

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/cookieValue")
  6. public String cookieValue(@CookieValue(value="JSESSIONID")
  7. String sessionid){
  8. System.out.println("sessionid:" + sessionid);
  9. return "success";
  10. }
  11. }

運行結果:

圖27-08

27.2.7 使用實體類對象接收請求參數值

如果處理方法的參數是一個實體類對象,那么SpringMVC會將請求的參數名與實體類對象的屬性進行匹配,為實體類對象的屬性賦值,並且支持級聯屬性的賦值。以下是具體的示例:

實體類:Student.java

復制
  1. public class Student
  2. {
  3. private String stuName;
  4. private int stuAge;
  5. private Address address ;
  6. //setter、getter
  7. @Override
  8. public String toString()
  9. {
  10. return "姓名:"+this.stuName+"\t年齡:"+this.stuAge
  11. +"\t家庭地址:"+this.address.getHomeAddress()
  12. +"\t學校地址:"+this.address.getSchoolAddress();
  13. }
  14. }

實體類:Address.java

復制
  1. public class Address
  2. {
  3. private String schoolAddress;
  4. private String homeAddress;
  5. //setter、getter
  6. }

form表單中使用實體類的屬性名作為<input>標簽的name值(可使用級聯屬性):

請求頁:index.jsp

復制
  1. <form action="FirstSpringDemo/entityProperties">
  2. 姓名:<input type="text" name="stuName"/><br>
  3. 年齡:<input type="text" name="stuAge"/><br>
  4. <!-- 使用級聯屬性 -->
  5. 家庭地址:<input type="text" name="address.homeAddress"/><br>
  6. 學校地址:<input type="text" name="address.schoolAddress"/><br>
  7. <input type="submit" value="提交"/>
  8. </form>

請求處理類:FirstSpringDemo.java

復制
  1. // import…
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. //使用實體類對象接收請求參數值(form表單中提交的數據)
  7. @RequestMapping("/entityProperties")
  8. public String entityProperties(Student student){
  9. System.out.println(student);
  10. return "success";
  11. }
  12. }

執行index.jsp,如下

圖27-09

點擊提交后,控制台的輸出結果:

圖27-10

27.2.8 使用Servlet API作為參數

如果想使用原生的Servlet API進行開發,只需要將Servlet API放入方法的參數中,如下:

復制
  1. //使用Servlet API開發
  2. @RequestMapping("/developWithServletAPI")
  3. public String developWithServletAPI(HttpServletRequest requst,
  4. HttpServletResponse response, HttpSession session)
  5. {
  6. //使用request和response參數處理請求或響應...
  7. return "success";
  8. }

27.3 處理模型數據

假設需要從數據庫查詢數據:在MVC設計模式中,用戶從視圖頁面(V)發起一個請求到控制器(C),控制器調用Service/Dao等處理數據,並從數據庫中返回數據(M)。之后,控制器(C)拿到數據(M)后加以處理,並返回到視圖頁面(V)。

SpringMVC提供了四種途徑來處理帶數據的視圖(M和V):ModelAndViewModelMapMapModel@SessionAttributes@ModelAttribute

27.3.1 ModelAndView

ModelAndView包含了Model(M)和View(V)兩部分。用法如下:

index.jsp

復制
  1. <a href="FirstSpringDemo/testModelAndView">testModelAndView</a>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testModelAndView")
  6. public ModelAndView testModelAndView(){
  7. String view = "success";
  8. ModelAndView mav= new ModelAndView(view);
  9. Student student = new Student("張三",23);
  10. //添加student對象數據放入ModelAndView中
  11. mav.addObject("student ",student);
  12. return mav;
  13. }
  14. }

通過ModelAndView的構造方法將視圖頁面的名稱”success”放入mav對象,再通過addObject()方法將數據放入mav對象,最后返回mav。之后,程序就會跳轉到mav指定的視圖頁面views/success.jsp(仍然會被視圖解析器加上了前綴和后綴),並將mav中的數據student放入request作用於之中。

返回的視圖頁面:success.jsp

復制
  1. <body>
  2. ${requestScope.student.stuName }
  3. </body>

執行index.jsp中的超鏈接,運行結果:

圖27-11

27.3.2 使用MapModelMapModel作為方法的參數處理數據

可以給SpringMVC的請求處理方法增加一個Map類型的參數。如果向此Map中增加數據,那么該數據也會被放入request作用域中。

index.jsp

<a href="FirstSpringDemo/testMap">testMap</a>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testMap")
  6. public String testMap(Map<String, Object> map)
  7. {
  8. Student student = new Student("張三", 23);
  9. map.put("student", student);
  10. return "success";
  11. }
  12. }

返回的視圖頁面success.jsp及運行結果同上例。

除了Map以外,還可以給請求處理方法增加一個ModelMapModel類型的參數,效果完全一樣,如下:

使用ModelMap類型的參數

復制
  1. @RequestMapping("/testModelMap")
  2. public String testMap(ModelMap map)
  3. {
  4. Student student = new Student("張三", 23);
  5. map.put("student", student);
  6. return "success";
  7. }

使用Model類型的參數

復制
  1. @RequestMapping("/testModel")
  2. public String testModel(Model map)
  3. {
  4. Student student = new Student("張三", 23);
  5. map.addAttribute("student", student);
  6. return "success";
  7. }

27.3.3 使用@SessionAttributes處理數據

我們已經知道,向ModelAndView以及MapModelMapModel參數中增加數據時,數據會同時被放到request作用域中。如果還要把數據放到session作用域中,就需要使用@SessionAttributes注解,如下:

index.jsp

復制
  1. <a href="FirstSpringDemo/testSessionAttribute">
  2. testSessionAttribute
  3. </a>

請求處理類:FirstSpringDemo.java

復制
  1. @SessionAttributes(value="student")
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @RequestMapping("/testSessionAttribute")
  7. public String testSessionAttribute(Map<String ,Object> map){
  8. Student student = new Student("張三", 23);
  9. map.put("student", student);
  10. return "success";
  11. }
  12. }

在類的上方加入@SessionAttributes(value="student"),表示將request作用域中的"student"對象同時加入到session作用域之中。

返回的視圖頁面:success.jsp

復制
  1. <body>
  2. request作用域中:${requestScope.student.stuName } <br/>
  3. session作用域中:${sessionScope.student.stuName } <br/>
  4. </body>

執行index.jsp中的超鏈接,運行結果:

圖27-12

@SessionAttributes除了可以使用value將指定的對象名加入到session范圍,還可以使用types將某一個類型的對象都加入到session范圍,如下:

請求處理類:FirstSpringDemo.java

復制
  1. @SessionAttributes(types=Student.class)
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @RequestMapping("/testSessionAttribute")
  7. public String testSessionAttribute(Map<String ,Object> map)
  8. {
  9. Student student = new Student("張三", 23);
  10. map.put("student", student);
  11. return "success";
  12. }
  13. }

通過@SessionAttributes(types=Student.class) 將request作用域中Student類型的對象同時加入到session作用域之中。

27.3.4 使用@ModelAttribute注解處理數據

假設數據庫中存在一條學生信息,如下:

圖27-13

現在我們需要修改學生的年齡(姓名等其他信息不變),先嘗試用以下方式完成:

index.jsp(修改學號為31號的學生年齡)

復制
  1. <form action="FirstSpringDemo/testModelAttribute" method="post">
  2. <input type="hidden" value="31" name="stuNo"/>
  3. 年齡:<input type="text" name="stuAge"/><br>
  4. <input type="submit" value="修改"/>
  5. </form>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testModelAttribute")
  6. public String testModelAttribute(Student student){
  7. //省略數據庫的更新操作:將數據表中stuNo=31的學生信息,更新為參數student中的各屬性值
  8. System.out.println("更新后的學生信息:姓名: "
  9. + student.getStuName()+",年齡:"+student.getStuAge());
  10. return "success";
  11. }
  12. }

執行index.jsp,並將年齡修改為66歲,如圖:

圖27-14

點擊修改,控制台的輸出結果如下:

圖27-15

可以發現,年齡確實成功修改了,但是姓名卻變成了null。這是因為在index.jspform表單中,只提交了stuAge字段的屬性,而不存在stuName等其他字段的屬性,因此stuAge屬性會從輸入框中獲取,而其他屬性值會使用相應類型的默認值(如String類型的stuName默認值就是null)。

這與我們的本意不符,我們的本意是:被修改的屬性,使用修改后的值(如stuAge);而沒被修改的屬性,要使用數據庫中原有的值(如果stuName應該保留“張三”)。要想實現我們的“本意”,可以在請求控制類中增加一個用@ModelAttribute的方法,如下:

請求處理類:FirstSpringDemo.java

復制
  1. //@SessionAttributes(types=Student.class)
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @ModelAttribute
  7. public void queryStudentBeforeUpdate (int stuNo,Map<String,
  8. Object> map)
  9. {
  10. //使用帶數據的實體類對象,模擬從數據庫中獲取學號為stuNo的學生對象
  11. Student student = new Student();
  12. student.setStuNo(stuNo);
  13. student.setStuName("張三");
  14. student.setStuAge(23);
  15. //即用以上語句模擬Student student
  16. = stuService.queryStudentByNo(stuNo);
  17. //將從數據庫中查詢的student對象放入map中
  18. map.put("student", student);
  19. }
  20. @RequestMapping("/testModelAttribute")
  21. public String testModelAttribute(Student student){
  22. //省略數據庫的更新操作
  23. System.out.println("更新后的學生信息:姓名: " + student.getStuName()+",年齡:"+student.getStuAge());
  24. return "success";
  25. }
  26. }

重新提交之前的form表單,控制台輸出結果如下:

圖27-16

可以發現,不但stuAge得到了修改,並且stuName也保留了原來的值。

@ModelAttribute的應用邏輯是:

@ModelAttribute修飾的方法(如queryStudentBeforeUpdate())會在請求處理方法(如testModelAttribute())調用之前被執行:具體是,如果請求處理方法有輸入參數(如student),則程序會在@ModelAttribute修飾的方法中的map對象里,尋找map中的key值是否與請求處理方法的參數名①一致,如果一致(如map中有名為”student”的key,testModelAttribute()方法也有名為”student”的參數)就會用參數student中不為null的屬性值(如stuNo=31,stuAge=66)去覆蓋map中的student對象值,最后使用的是覆蓋后的student對象。例如,map中的student對象值是:“學號:31,姓名:張三,年齡23”,參數中student的對象值是:“學號31,姓名null,年齡66”,因此用參數student不為null的屬性值(stuNo=31,stuAge=66)去覆蓋map中student屬性值的結果就是:“學號31,姓名:張三,年齡66”。即form表單傳來的stuNostuAge屬性得到了修改,而form表單中不存在的stuName屬性則保持不變。如果map中的key值與請求處理方法的參數名不一致,則需要在參數前使用@ModelAttribute標識出map中對應的key值,如下:

請求處理類:FirstSpringDemo.java

復制
  1. @ModelAttribute
  2. public void queryStudentBeforeUpdate(int stuNo,Map<String,
  3. Object> map)
  4. {
  5. ...
  6. map.put("stu", student);
  7. }
  8. @RequestMapping("/testModelAttribute")
  9. public String testModelAttribute(
  10. @ModelAttribute("stu")Student student){
  11. ...
  12. return "success";
  13. }

map中的keystu,與方法的參數名student不一致,就需要在參數名前使用@ModelAttribute("stu")來進行標識。

①參數名:實際是判斷是否與“首字母小寫的參數類型”一致。如參數的類型是Student,則會判斷是否與首字母小寫的參數類型(即student)一致。此段落中,用“參數名”來代替“首字母小寫的參數類型”僅僅是為了便於讀者閱讀。

說明:

標有@ModelAttribute注解的方法,會在請求處理類中的每一個方法執行前,都執行一次,因此需要謹慎使用。

27.1.1 @RequestMapping使用

之前,我們是把@RequestMapping注解放在方法之上,用來給方法綁定一個請求映射。除此以外,@RequestMapping注解還可以放在類的上面。例如,我們給之前的請求處理類(FirstSpringDemo.java)的類名上也加一個@RequestMapping注解,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping("/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/firstSpringMVC")
  6. public String welcomeToSpringMVC()
  7. {
  8. System.out.println("welcome to springMVC");
  9. return "success";
  10. }
  11. }

類前面加了@RequestMapping注解以后,前端發來的請求就不能再直接去匹配方法上面的@RequestMapping了。而是應該先匹配類前的@RequestMapping值,再匹配方法前的@RequestMapping

因此,方法前面@RequestMapping的值,是相對於類前的@RequestMapping值。如果類的前面不存在@RequestMapping,則方法前的@RequestMapping值就是相對於項目根目錄。 例如,在類前加了@ RequestMapping("/FirstSpringDemo")以后,前台就必須通過以下路徑來訪問:

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/firstSpringMVC">
  3. My First SpringMVC Demo
  4. </a>
  5. </body>

即先通過“FirstSpringDemo”匹配類前的@RequestMapping("/FirstSpringDemo") ,再通過“ firstSpringMVC”匹配方法前的@RequestMapping("/firstSpringMVC")

27.1.2 @RequestMapping屬性

@RequestMapping注解的常用屬性如下,

屬性 簡介
value 指定請求的實際URL地址,屬性名value可省。例如@RequestMapping("/firstSpringMVC")等價於@RequestMapping(value="/firstSpringMVC")
method 指定請求方式,包含 GET(默認)、POST、PUT、DELETE等;可以通過枚舉類RequestMethod設置,如method=RequestMethod.POST。
params 規定請求中的些參數值,必須滿足一定的條件。params本身是一個字符串數組。
headers 規定請求中的請求頭(header)必須滿足一定的條件。

(1)method屬性

例如,因為超鏈接本身是采用GET方式提交請求。因此,若前台仍然是通過<ahref="FirstSpringDemo/firstSpringMVC">...</a>發送請求,則處理類必須使用GET方式才能接受到此請求。如果使用“POST”等其他方式,是無法獲取到該請求的,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/firstSpringMVC"
  6. ,method=RequestMethod.POST)
  7. public String welcomeToSpringMVC()
  8. {
  9. return "success";
  10. }
  11. }

如果再點擊上面超鏈接,運行結果如下,

圖27-01

提示我們“請求方法不支持GET方式”。

如果把超鏈接,替換成以下POST方式的表單提交,

index.jsp

復制
  1. <body>
  2. <form action="FirstSpringDemo/firstSpringMVC" method="POST">
  3. <input type="submit" value="POST方式提交"/>
  4. </form>
  5. </body>

點擊“POST方式提交”后,就又會正常運行。

(2)params屬性

例如,我們通過超鏈接加入兩個請求參數,如下,

index.jsp

復制
  1. <body>
  2. <a href=
  3. "FirstSpringDemo/requestWithParams?name=zhangsan&age=20">
  4. requestWithParams...
  5. </a>
  6. </body>

再通過params來檢查請求中的參數是否符合要求,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithParams"
  6. ,params={"name","age!=23"})
  7. public String requestWithParams()
  8. {
  9. return "success";
  10. }
  11. }

以上請求通過params規定請求參數必須包含“name”參數,並且“age!=23”,我們之前發來的請求“…?name=zhangsan&age=20”符合要求,因此可以被該方法接收並處理。如果發送的請求參數是“…?name=zhangsan&age=23”或“…? age=23”等不符合params規定,就會引發我們所熟悉 “404”異常。

params支持以下表達式:

表達式 簡介
paramName 必須包含參數名為“paramName”的參數。
!paramName 不能包含參數名為“paramName”的參數。
paramName!=paramValue 必須包含參數名為“paramName”的參數,但參數值不能是“paramValue”。

(3)headers屬性

SpringMVC用headers來約束“參數”,用headers來約束“請求頭”。我們可以在火狐瀏覽器里打開“firebug”查看每一次請求的“請求頭”,如下,

圖27-02

“請求頭”指明了請求所攜帶的MIME類型、字符集等信息。

例如,可以通過“headers”指定請求頭中的“Accept-Language”必須是“zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3”,以及“Accept-Encoding”必須是“gzip, deflate”,如下

FirstSpringDemo.java

復制
  1. @RequestMapping(value="/requestWithHeaders",
  2. headers={"Accept-Language=zh-CN,zh;
  3. q=0.8,en-US;q=0.5,en;q=0.3",
  4. "Accept-Encoding=gzip, deflate"})
  5. public String requestWithHeaders()
  6. {
  7. return "success";
  8. }

關於“請求頭”的知識,讀者可以查閱相關資料,本書不作為重點講解。

27.2 基於@RequestMapping的配置

27.2.1 Ant風格的請求路徑

SpringMVC除了支持傳統方式的請求外,還支持Ant風格的請求路徑。

Ant風格的請求路徑支持以下3種通配符:

通配符 簡介
? 匹配任何單字符
* 匹配0或者任意數量的字符
** 匹配0或者更多的目錄

例如,在處理方法前配置@RequestMapping(value="/requestWithAntPath/*/test"),表示請求路徑的“requestWithAntPath”和“test”之間可以填任意字符,如下,

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithAntPath/*/test")
  6. public String requestWithAntPath()
  7. {
  8. return "success";
  9. }
  10. }

如果前端發送以下請求,是可以匹配到requestWithAntPath()方法的。

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/requestWithAntPath/lanqiao/test">
  3. requestWithAntPath...
  4. </a>
  5. </body>

其他Ant風格的示例如下表,

請求路徑 匹配的示例
/requestWithAntPath/**/test /requestWithAntPath/a/b/test、/requestWithAntPath/test等
/requestWithAntPath/test?? /requestWithAntPath/testxy、/requestWithAntPath/testqq等

27.2.2 使用@PathVariable獲取動態參數

在SpringMVC中,可以使用@PathVariable來獲得請求路徑中的動態參數。

如下,通過前端傳入一個參數“9527”,

index.jsp

復制
  1. <body>
  2. <a href="FirstSpringDemo/requestWithPathVariable/9527">
  3. requestWithPathVariable...
  4. </a>
  5. </body>

處理方法就可以通過@PathVariable來獲取此參數值,如下

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping(value="/requestWithPathVariable/{id}")
  6. public String
  7. requestWithPathVariable(@PathVariable("id") Integer id)
  8. {
  9. System.out.println("id:"+id);
  10. return "success";
  11. }}

具體是通過@RequestMapping(value="/requestWithPathVariable/{id}")中的占位符“{id}”接收到參數值“9527”,再把參數值傳給@PathVariable("id")中的“id”,最后再把值賦值給方法的參數id

27.2.3 REST風格

REST(Representational State Transfer)是一種編程風格,可以顯著降低開發的復雜性,是當前非常流行的一種互聯網軟件架構。

在學習REST之前,我們首先要知道,在HTTP協議里面有多種請求方式,並且其中的POSTDELETEPUTGET等四個方式,分別對應增刪改查四種操作,具體是:POST對應“增”,DELETE對應“刪”、PUT對應“改”,GET對應“查”。但是普通瀏覽器中的form表單,只支持GETPOST兩種請求方式。為了使普通瀏覽器支持PUTDELETE方式,可以使用Spring提供的過濾器HiddenHttpMethodFilter,此過濾器可以通過一定的規則,將部分POST請求轉換為PUTDELETE請求。如果讀者想了解HiddenHttpMethodFilter的底層代碼,可以閱讀spring-web-x.x.xRELEASE.jar包中HiddenHttpMethodFilter類里的doFilterInternal()方法。

實現PUTDELETE請求方式的步驟如下:

1.web.xml中配置HiddenHttpMethodFilter過濾器,如下,

web.xml

復制
  1. <web-app …>
  2. <filter>
  3. <filter-name>HiddenHttpMethodFilter</filter-name>
  4. <filter-class>
  5. org.springframework.web.filter.HiddenHttpMethodFilter
  6. </filter-class>
  7. </filter>
  8. <filter-mapping>
  9. <filter-name>HiddenHttpMethodFilter</filter-name>
  10. <url-pattern>/*</url-pattern>
  11. </filter-mapping>
  12. </web-app>

2.form表單中指定請求方式為method="post";並在表單中增加一個hidden隱藏域,且設置隱藏域的namevalue屬性:name="_method"value="PUT"value="DELETE"

3.在處理方法的@RequestMapping注解中,用method屬性指定請求方式(如method=RequestMethod.DELETEmethod=RequestMethod.PUT等)。

例如,在web.xml中配置了HiddenHttpMethodFilter以后,就可以使用下面的方式發送並處理增、刪、改、查的請求:

①發送請求

index.jsp

復制
  1. <body>
  2. <form action="FirstSpringDemo/requestWithREST/9527"
  3. method="post">
  4. <input type="hidden" name="_method" value="DELETE" />
  5. <input type="submit" value="刪除" />
  6. </form>
  7. <form action="FirstSpringDemo/requestWithREST/9527"
  8. method="post">
  9. <input type="hidden" name="_method" value="PUT" />
  10. <input type="submit" value="修改" />
  11. </form>
  12. <form action="FirstSpringDemo/requestWithREST/9527"
  13. method="post">
  14. <input type="submit" value="增加" />
  15. </form>
  16. <a href="FirstSpringDemo/requestWithREST/9527">查看</a>
  17. </body>

②處理請求

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. //使用REST風格,處理“刪除”的請求
  6. @RequestMapping(value="/requestWithREST/{id}",
  7. method=RequestMethod.DELETE)
  8. public String requestWithRestDelete(@PathVariable("id")
  9. Integer id)
  10. {
  11. System.out.println("刪除時需要的id:"+id);
  12. return "success";
  13. }
  14. //使用REST風格,處理“修改”的請求
  15. @RequestMapping(value="/requestWithREST/{id}",
  16. method=RequestMethod.PUT)
  17. public String requestWithRestPut(@PathVariable("id")
  18. Integer id)
  19. {
  20. System.out.println("修改時需要的id:"+id);
  21. return "success";
  22. }
  23. //使用REST風格,處理“增加”的請求
  24. @RequestMapping(value="/requestWithREST/{id}",
  25. method=RequestMethod.POST)
  26. public String requestWithRestAdd(@PathVariable("id")
  27. Integer id)
  28. {
  29. System.out.println("增加時需要的id:"+id);
  30. return "success";
  31. }
  32. //使用REST風格,處理“查看”的請求
  33. @RequestMapping(value="/requestWithREST/{id}",
  34. method=RequestMethod.GET)
  35. public String requestWithRestGet(@PathVariable("id") Integer id)
  36. {
  37. System.out.println("查詢時需要的id:"+id);
  38. return "success";
  39. }
  40. }

運行index.jsp頁面,如圖,

圖27-03

依次點擊刪除、修改、增加、查看按鈕,可在控制台得到以下結果:

圖27-04

27.2.4 使用@RequestParam獲取請求參數

Spring MVC可以通過@RequestParam來接收請求中的參數值,該注解有三個常用的屬性:

屬性名 簡介
value 請求攜帶參數的參數名
required 標識請求參數中是否必須存在某個具體的參數。 true(默認):必須存在;若不存在,將拋出異常。 false:不必須。
defaultValue 給參數賦一個默認值。如果請求中不存在某個參數,則該參數就取defaultValue所設置的值。

index.jsp

復制
  1. <a href="FirstSpringDemo/requestParam?name=zhangsan&age=23">
  2. TestRequestParam
  3. </a>

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value="/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. //使用@RequestParam注解接收請求參數
  6. @RequestMapping("/requestParam")
  7. public String requestParam(@RequestParam(value="name") String name, @RequestParam(value="age") Integer age)
  8. {
  9. System.out.println("name: " + name + " age: " + age);
  10. return "success";
  11. }
  12. }…

@RequestParam通過value值與傳入的參數名匹配,並將參數值賦值給@RequestParam后面的變量。例如,通過@RequestParam(value="name")接收index.jsp傳來的name參數值(即zhangsan),並將參數值(zhangsan)賦值給@RequestParam后面的String name,類似於String name="zhangsan"

若將請求中的age參數刪除,如下

index.jsp

復制
  1. <a href="FirstSpringDemo/requestParam?name=zhangsan">
  2. TestRequestParam
  3. </a>

再次執行以上超鏈接,則會發生異常,如下:

圖27-05

為了解決此異常,就可以給age參數的@RequestParam加入required=false,如下:

FirstSpringDemo.java

復制
  1. @RequestMapping("/requestParam")
  2. public String requestParam(@RequestParam(value = "name") String name, @RequestParam(value = "age",required=false) Integer age)
  3. {
  4. }

此外,還可以通過@RequestParamdefaultValue屬性給請求參數設置默認值,如下,

FirstSpringDemo.java

復制
  1. @RequestMapping("/requestParam")
  2. public String requestParam(@RequestParam(value = "name")
  3. String name, @RequestParam(value = "age",
  4. required=false,defaultValue="23") Integer age)
  5. {
  6. System.out.println("name: " + name + " age: " + age);
  7. return "success";
  8. }…

通過defaultValue="23"age的默認值設置為”23”,即如果前端發送的請求中沒有攜帶age參數,則age的值就是23。

27.2.5 @RequestHeader注解

在HTTP協議中,每一次請求都會攜帶相關的“頭信息”,例如可以在fireBug中觀察到以下頭信息:

圖27-06

SpringMVC也提供了@RequestHeader注解來幫助我們獲取請求中的“頭信息”,如下:

index.jsp

<a href="FirstSpringDemo/requestHeader"> requestHeader</a><br/>

FirstSpringDemo

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/requestHeader")
  6. public String
  7. requestHeader(@RequestHeader(value="Accept-Language")
  8. String al){
  9. System.out.println("Accept-Language:" + al);
  10. return "success";
  11. }
  12. }

通過@RequestHeader獲取“頭信息”,並通過value屬性指定獲取頭信息中的Accept-Language值,並把值賦值給al參數。

執行index.jsp中的requestHeader超鏈接,可在控制台得到以下結果:

圖27-07

27.2.6 @CookieValue注解

@CookieValue可以給處理方法入參綁定某個Cookie值。例如,客戶端有一個名為JSESSIONID的Cookie對象,服務端可以通過@CookieValue來獲取此JSESSIONID的值:

index.jsp

<a href="FirstSpringDemo/cookieValue">cookieValue</a><br/>

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/cookieValue")
  6. public String cookieValue(@CookieValue(value="JSESSIONID")
  7. String sessionid){
  8. System.out.println("sessionid:" + sessionid);
  9. return "success";
  10. }
  11. }

運行結果:

圖27-08

27.2.7 使用實體類對象接收請求參數值

如果處理方法的參數是一個實體類對象,那么SpringMVC會將請求的參數名與實體類對象的屬性進行匹配,為實體類對象的屬性賦值,並且支持級聯屬性的賦值。以下是具體的示例:

實體類:Student.java

復制
  1. public class Student
  2. {
  3. private String stuName;
  4. private int stuAge;
  5. private Address address ;
  6. //setter、getter
  7. @Override
  8. public String toString()
  9. {
  10. return "姓名:"+this.stuName+"\t年齡:"+this.stuAge
  11. +"\t家庭地址:"+this.address.getHomeAddress()
  12. +"\t學校地址:"+this.address.getSchoolAddress();
  13. }
  14. }

實體類:Address.java

復制
  1. public class Address
  2. {
  3. private String schoolAddress;
  4. private String homeAddress;
  5. //setter、getter
  6. }

form表單中使用實體類的屬性名作為<input>標簽的name值(可使用級聯屬性):

請求頁:index.jsp

復制
  1. <form action="FirstSpringDemo/entityProperties">
  2. 姓名:<input type="text" name="stuName"/><br>
  3. 年齡:<input type="text" name="stuAge"/><br>
  4. <!-- 使用級聯屬性 -->
  5. 家庭地址:<input type="text" name="address.homeAddress"/><br>
  6. 學校地址:<input type="text" name="address.schoolAddress"/><br>
  7. <input type="submit" value="提交"/>
  8. </form>

請求處理類:FirstSpringDemo.java

復制
  1. // import…
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. //使用實體類對象接收請求參數值(form表單中提交的數據)
  7. @RequestMapping("/entityProperties")
  8. public String entityProperties(Student student){
  9. System.out.println(student);
  10. return "success";
  11. }
  12. }

執行index.jsp,如下

圖27-09

點擊提交后,控制台的輸出結果:

圖27-10

27.2.8 使用Servlet API作為參數

如果想使用原生的Servlet API進行開發,只需要將Servlet API放入方法的參數中,如下:

復制
  1. //使用Servlet API開發
  2. @RequestMapping("/developWithServletAPI")
  3. public String developWithServletAPI(HttpServletRequest requst,
  4. HttpServletResponse response, HttpSession session)
  5. {
  6. //使用request和response參數處理請求或響應...
  7. return "success";
  8. }

27.3 處理模型數據

假設需要從數據庫查詢數據:在MVC設計模式中,用戶從視圖頁面(V)發起一個請求到控制器(C),控制器調用Service/Dao等處理數據,並從數據庫中返回數據(M)。之后,控制器(C)拿到數據(M)后加以處理,並返回到視圖頁面(V)。

SpringMVC提供了四種途徑來處理帶數據的視圖(M和V):ModelAndViewModelMapMapModel@SessionAttributes@ModelAttribute

27.3.1 ModelAndView

ModelAndView包含了Model(M)和View(V)兩部分。用法如下:

index.jsp

復制
  1. <a href="FirstSpringDemo/testModelAndView">testModelAndView</a>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testModelAndView")
  6. public ModelAndView testModelAndView(){
  7. String view = "success";
  8. ModelAndView mav= new ModelAndView(view);
  9. Student student = new Student("張三",23);
  10. //添加student對象數據放入ModelAndView中
  11. mav.addObject("student ",student);
  12. return mav;
  13. }
  14. }

通過ModelAndView的構造方法將視圖頁面的名稱”success”放入mav對象,再通過addObject()方法將數據放入mav對象,最后返回mav。之后,程序就會跳轉到mav指定的視圖頁面views/success.jsp(仍然會被視圖解析器加上了前綴和后綴),並將mav中的數據student放入request作用於之中。

返回的視圖頁面:success.jsp

復制
  1. <body>
  2. ${requestScope.student.stuName }
  3. </body>

執行index.jsp中的超鏈接,運行結果:

圖27-11

27.3.2 使用MapModelMapModel作為方法的參數處理數據

可以給SpringMVC的請求處理方法增加一個Map類型的參數。如果向此Map中增加數據,那么該數據也會被放入request作用域中。

index.jsp

<a href="FirstSpringDemo/testMap">testMap</a>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testMap")
  6. public String testMap(Map<String, Object> map)
  7. {
  8. Student student = new Student("張三", 23);
  9. map.put("student", student);
  10. return "success";
  11. }
  12. }

返回的視圖頁面success.jsp及運行結果同上例。

除了Map以外,還可以給請求處理方法增加一個ModelMapModel類型的參數,效果完全一樣,如下:

使用ModelMap類型的參數

復制
  1. @RequestMapping("/testModelMap")
  2. public String testMap(ModelMap map)
  3. {
  4. Student student = new Student("張三", 23);
  5. map.put("student", student);
  6. return "success";
  7. }

使用Model類型的參數

復制
  1. @RequestMapping("/testModel")
  2. public String testModel(Model map)
  3. {
  4. Student student = new Student("張三", 23);
  5. map.addAttribute("student", student);
  6. return "success";
  7. }

27.3.3 使用@SessionAttributes處理數據

我們已經知道,向ModelAndView以及MapModelMapModel參數中增加數據時,數據會同時被放到request作用域中。如果還要把數據放到session作用域中,就需要使用@SessionAttributes注解,如下:

index.jsp

復制
  1. <a href="FirstSpringDemo/testSessionAttribute">
  2. testSessionAttribute
  3. </a>

請求處理類:FirstSpringDemo.java

復制
  1. @SessionAttributes(value="student")
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @RequestMapping("/testSessionAttribute")
  7. public String testSessionAttribute(Map<String ,Object> map){
  8. Student student = new Student("張三", 23);
  9. map.put("student", student);
  10. return "success";
  11. }
  12. }

在類的上方加入@SessionAttributes(value="student"),表示將request作用域中的"student"對象同時加入到session作用域之中。

返回的視圖頁面:success.jsp

復制
  1. <body>
  2. request作用域中:${requestScope.student.stuName } <br/>
  3. session作用域中:${sessionScope.student.stuName } <br/>
  4. </body>

執行index.jsp中的超鏈接,運行結果:

圖27-12

@SessionAttributes除了可以使用value將指定的對象名加入到session范圍,還可以使用types將某一個類型的對象都加入到session范圍,如下:

請求處理類:FirstSpringDemo.java

復制
  1. @SessionAttributes(types=Student.class)
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @RequestMapping("/testSessionAttribute")
  7. public String testSessionAttribute(Map<String ,Object> map)
  8. {
  9. Student student = new Student("張三", 23);
  10. map.put("student", student);
  11. return "success";
  12. }
  13. }

通過@SessionAttributes(types=Student.class) 將request作用域中Student類型的對象同時加入到session作用域之中。

27.3.4 使用@ModelAttribute注解處理數據

假設數據庫中存在一條學生信息,如下:

圖27-13

現在我們需要修改學生的年齡(姓名等其他信息不變),先嘗試用以下方式完成:

index.jsp(修改學號為31號的學生年齡)

復制
  1. <form action="FirstSpringDemo/testModelAttribute" method="post">
  2. <input type="hidden" value="31" name="stuNo"/>
  3. 年齡:<input type="text" name="stuAge"/><br>
  4. <input type="submit" value="修改"/>
  5. </form>

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testModelAttribute")
  6. public String testModelAttribute(Student student){
  7. //省略數據庫的更新操作:將數據表中stuNo=31的學生信息,更新為參數student中的各屬性值
  8. System.out.println("更新后的學生信息:姓名: "
  9. + student.getStuName()+",年齡:"+student.getStuAge());
  10. return "success";
  11. }
  12. }

執行index.jsp,並將年齡修改為66歲,如圖:

圖27-14

點擊修改,控制台的輸出結果如下:

圖27-15

可以發現,年齡確實成功修改了,但是姓名卻變成了null。這是因為在index.jspform表單中,只提交了stuAge字段的屬性,而不存在stuName等其他字段的屬性,因此stuAge屬性會從輸入框中獲取,而其他屬性值會使用相應類型的默認值(如String類型的stuName默認值就是null)。

這與我們的本意不符,我們的本意是:被修改的屬性,使用修改后的值(如stuAge);而沒被修改的屬性,要使用數據庫中原有的值(如果stuName應該保留“張三”)。要想實現我們的“本意”,可以在請求控制類中增加一個用@ModelAttribute的方法,如下:

請求處理類:FirstSpringDemo.java

復制
  1. //@SessionAttributes(types=Student.class)
  2. @Controller
  3. @RequestMapping(value = "/FirstSpringDemo")
  4. public class FirstSpringDemo
  5. {
  6. @ModelAttribute
  7. public void queryStudentBeforeUpdate (int stuNo,Map<String,
  8. Object> map)
  9. {
  10. //使用帶數據的實體類對象,模擬從數據庫中獲取學號為stuNo的學生對象
  11. Student student = new Student();
  12. student.setStuNo(stuNo);
  13. student.setStuName("張三");
  14. student.setStuAge(23);
  15. //即用以上語句模擬Student student
  16. = stuService.queryStudentByNo(stuNo);
  17. //將從數據庫中查詢的student對象放入map中
  18. map.put("student", student);
  19. }
  20. @RequestMapping("/testModelAttribute")
  21. public String testModelAttribute(Student student){
  22. //省略數據庫的更新操作
  23. System.out.println("更新后的學生信息:姓名: " + student.getStuName()+",年齡:"+student.getStuAge());
  24. return "success";
  25. }
  26. }

重新提交之前的form表單,控制台輸出結果如下:

圖27-16

可以發現,不但stuAge得到了修改,並且stuName也保留了原來的值。

@ModelAttribute的應用邏輯是:

@ModelAttribute修飾的方法(如queryStudentBeforeUpdate())會在請求處理方法(如testModelAttribute())調用之前被執行:具體是,如果請求處理方法有輸入參數(如student),則程序會在@ModelAttribute修飾的方法中的map對象里,尋找map中的key值是否與請求處理方法的參數名①一致,如果一致(如map中有名為”student”的key,testModelAttribute()方法也有名為”student”的參數)就會用參數student中不為null的屬性值(如stuNo=31,stuAge=66)去覆蓋map中的student對象值,最后使用的是覆蓋后的student對象。例如,map中的student對象值是:“學號:31,姓名:張三,年齡23”,參數中student的對象值是:“學號31,姓名null,年齡66”,因此用參數student不為null的屬性值(stuNo=31,stuAge=66)去覆蓋map中student屬性值的結果就是:“學號31,姓名:張三,年齡66”。即form表單傳來的stuNostuAge屬性得到了修改,而form表單中不存在的stuName屬性則保持不變。如果map中的key值與請求處理方法的參數名不一致,則需要在參數前使用@ModelAttribute標識出map中對應的key值,如下:

請求處理類:FirstSpringDemo.java

復制
  1. @ModelAttribute
  2. public void queryStudentBeforeUpdate(int stuNo,Map<String,
  3. Object> map)
  4. {
  5. ...
  6. map.put("stu", student);
  7. }
  8. @RequestMapping("/testModelAttribute")
  9. public String testModelAttribute(
  10. @ModelAttribute("stu")Student student){
  11. ...
  12. return "success";
  13. }

map中的keystu,與方法的參數名student不一致,就需要在參數名前使用@ModelAttribute("stu")來進行標識。

①參數名:實際是判斷是否與“首字母小寫的參數類型”一致。如參數的類型是Student,則會判斷是否與首字母小寫的參數類型(即student)一致。此段落中,用“參數名”來代替“首字母小寫的參數類型”僅僅是為了便於讀者閱讀。

說明:

標有@ModelAttribute注解的方法,會在請求處理類中的每一個方法執行前,都執行一次,因此需要謹慎使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM