27.1.1 @RequestMapping使用
之前,我們是把@RequestMapping注解放在方法之上,用來給方法綁定一個請求映射。除此以外,@RequestMapping注解還可以放在類的上面。例如,我們給之前的請求處理類(FirstSpringDemo.java)的類名上也加一個@RequestMapping注解,如下,
FirstSpringDemo.java
…@Controller@RequestMapping("/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/firstSpringMVC")public String welcomeToSpringMVC(){System.out.println("welcome to springMVC");return "success";}}
類前面加了@RequestMapping注解以后,前端發來的請求就不能再直接去匹配方法上面的@RequestMapping了。而是應該先匹配類前的@RequestMapping值,再匹配方法前的@RequestMapping。
因此,方法前面@RequestMapping的值,是相對於類前的@RequestMapping值。如果類的前面不存在@RequestMapping,則方法前的@RequestMapping值就是相對於項目根目錄。 例如,在類前加了@ RequestMapping("/FirstSpringDemo")以后,前台就必須通過以下路徑來訪問:
index.jsp
…<body><a href="FirstSpringDemo/firstSpringMVC">My First SpringMVC Demo</a></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
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/firstSpringMVC",method=RequestMethod.POST)public String welcomeToSpringMVC(){return "success";}}…
如果再點擊上面超鏈接,運行結果如下,

圖27-01
提示我們“請求方法不支持GET方式”。
如果把超鏈接,替換成以下POST方式的表單提交,
index.jsp
…<body><form action="FirstSpringDemo/firstSpringMVC" method="POST"><input type="submit" value="POST方式提交"/></form></body>…
點擊“POST方式提交”后,就又會正常運行。
(2)params屬性
例如,我們通過超鏈接加入兩個請求參數,如下,
index.jsp
…<body><a href="FirstSpringDemo/requestWithParams?name=zhangsan&age=20">requestWithParams...</a></body>…
再通過params來檢查請求中的參數是否符合要求,如下,
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithParams",params={"name","age!=23"})public String requestWithParams(){return "success";}}…
以上請求通過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
…@RequestMapping(value="/requestWithHeaders",headers={"Accept-Language=zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3","Accept-Encoding=gzip, deflate"})public String requestWithHeaders(){return "success";}…
關於“請求頭”的知識,讀者可以查閱相關資料,本書不作為重點講解。
27.2 基於@RequestMapping的配置
27.2.1 Ant風格的請求路徑
SpringMVC除了支持傳統方式的請求外,還支持Ant風格的請求路徑。
Ant風格的請求路徑支持以下3種通配符:
| 通配符 | 簡介 |
| ? | 匹配任何單字符 |
| * | 匹配0或者任意數量的字符 |
| ** | 匹配0或者更多的目錄 |
例如,在處理方法前配置@RequestMapping(value="/requestWithAntPath/*/test"),表示請求路徑的“requestWithAntPath”和“test”之間可以填任意字符,如下,
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithAntPath/*/test")public String requestWithAntPath(){return "success";}…}
如果前端發送以下請求,是可以匹配到requestWithAntPath()方法的。
index.jsp
…<body><a href="FirstSpringDemo/requestWithAntPath/lanqiao/test">requestWithAntPath...</a>…</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
…<body><a href="FirstSpringDemo/requestWithPathVariable/9527">requestWithPathVariable...</a></body>…
處理方法就可以通過@PathVariable來獲取此參數值,如下
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithPathVariable/{id}")public StringrequestWithPathVariable(@PathVariable("id") Integer id){System.out.println("id:"+id);return "success";}}…
具體是通過@RequestMapping(value="/requestWithPathVariable/{id}")中的占位符“{id}”接收到參數值“9527”,再把參數值傳給@PathVariable("id")中的“id”,最后再把值賦值給方法的參數id。
27.2.3 REST風格
REST(Representational State Transfer)是一種編程風格,可以顯著降低開發的復雜性,是當前非常流行的一種互聯網軟件架構。
在學習REST之前,我們首先要知道,在HTTP協議里面有多種請求方式,並且其中的POST、DELETE、PUT、GET等四個方式,分別對應增刪改查四種操作,具體是:POST對應“增”,DELETE對應“刪”、PUT對應“改”,GET對應“查”。但是普通瀏覽器中的form表單,只支持GET和POST兩種請求方式。為了使普通瀏覽器支持PUT和DELETE方式,可以使用Spring提供的過濾器HiddenHttpMethodFilter,此過濾器可以通過一定的規則,將部分POST請求轉換為PUT或DELETE請求。如果讀者想了解HiddenHttpMethodFilter的底層代碼,可以閱讀spring-web-x.x.xRELEASE.jar包中HiddenHttpMethodFilter類里的doFilterInternal()方法。
實現PUT或DELETE請求方式的步驟如下:
1.在web.xml中配置HiddenHttpMethodFilter過濾器,如下,
web.xml
…<web-app …><filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
2.在form表單中指定請求方式為method="post";並在表單中增加一個hidden隱藏域,且設置隱藏域的name及value屬性:name="_method"、value="PUT"或value="DELETE"。
3.在處理方法的@RequestMapping注解中,用method屬性指定請求方式(如method=RequestMethod.DELETE、method=RequestMethod.PUT等)。
例如,在web.xml中配置了HiddenHttpMethodFilter以后,就可以使用下面的方式發送並處理增、刪、改、查的請求:
①發送請求
index.jsp
…<body>…<form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="hidden" name="_method" value="DELETE" /><input type="submit" value="刪除" /></form><form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="hidden" name="_method" value="PUT" /><input type="submit" value="修改" /></form><form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="submit" value="增加" /></form><a href="FirstSpringDemo/requestWithREST/9527">查看</a></body>…
②處理請求
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{…//使用REST風格,處理“刪除”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.DELETE)public String requestWithRestDelete(@PathVariable("id")Integer id){System.out.println("刪除時需要的id:"+id);return "success";}//使用REST風格,處理“修改”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.PUT)public String requestWithRestPut(@PathVariable("id")Integer id){System.out.println("修改時需要的id:"+id);return "success";}//使用REST風格,處理“增加”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.POST)public String requestWithRestAdd(@PathVariable("id")Integer id){System.out.println("增加時需要的id:"+id);return "success";}//使用REST風格,處理“查看”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.GET)public String requestWithRestGet(@PathVariable("id") Integer id){System.out.println("查詢時需要的id:"+id);return "success";}}…
運行index.jsp頁面,如圖,

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

圖27-04
27.2.4 使用@RequestParam獲取請求參數
Spring MVC可以通過@RequestParam來接收請求中的參數值,該注解有三個常用的屬性:
| 屬性名 | 簡介 |
| value | 請求攜帶參數的參數名 |
| required | 標識請求參數中是否必須存在某個具體的參數。 true(默認):必須存在;若不存在,將拋出異常。 false:不必須。 |
| defaultValue | 給參數賦一個默認值。如果請求中不存在某個參數,則該參數就取defaultValue所設置的值。 |
index.jsp
…<a href="FirstSpringDemo/requestParam?name=zhangsan&age=23">TestRequestParam</a>…
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{//使用@RequestParam注解接收請求參數@RequestMapping("/requestParam")public String requestParam(@RequestParam(value="name") String name, @RequestParam(value="age") Integer age){System.out.println("name: " + name + " age: " + age);return "success";}}…
@RequestParam通過value值與傳入的參數名匹配,並將參數值賦值給@RequestParam后面的變量。例如,通過@RequestParam(value="name")接收index.jsp傳來的name參數值(即zhangsan),並將參數值(zhangsan)賦值給@RequestParam后面的String name,類似於String name="zhangsan"。
若將請求中的age參數刪除,如下
index.jsp
<a href="FirstSpringDemo/requestParam?name=zhangsan">TestRequestParam</a>
再次執行以上超鏈接,則會發生異常,如下:

圖27-05
為了解決此異常,就可以給age參數的@RequestParam加入required=false,如下:
FirstSpringDemo.java
…@RequestMapping("/requestParam")public String requestParam(@RequestParam(value = "name") String name, @RequestParam(value = "age",required=false) Integer age){…}…
此外,還可以通過@RequestParam的defaultValue屬性給請求參數設置默認值,如下,
FirstSpringDemo.java
…@RequestMapping("/requestParam")public String requestParam(@RequestParam(value = "name")String name, @RequestParam(value = "age",required=false,defaultValue="23") Integer age){System.out.println("name: " + name + " age: " + age);return "success";}…
通過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
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/requestHeader")public StringrequestHeader(@RequestHeader(value="Accept-Language")String al){System.out.println("Accept-Language:" + al);return "success";}}…
通過@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
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/cookieValue")public String cookieValue(@CookieValue(value="JSESSIONID")String sessionid){System.out.println("sessionid:" + sessionid);return "success";}}…
運行結果:

圖27-08
27.2.7 使用實體類對象接收請求參數值
如果處理方法的參數是一個實體類對象,那么SpringMVC會將請求的參數名與實體類對象的屬性進行匹配,為實體類對象的屬性賦值,並且支持級聯屬性的賦值。以下是具體的示例:
實體類:Student.java
public class Student{private String stuName;private int stuAge;private Address address ;//setter、getter@Overridepublic String toString(){return "姓名:"+this.stuName+"\t年齡:"+this.stuAge+"\t家庭地址:"+this.address.getHomeAddress()+"\t學校地址:"+this.address.getSchoolAddress();}}
實體類:Address.java
public class Address{private String schoolAddress;private String homeAddress;//setter、getter}
在form表單中使用實體類的屬性名作為<input>標簽的name值(可使用級聯屬性):
請求頁:index.jsp
<form action="FirstSpringDemo/entityProperties">姓名:<input type="text" name="stuName"/><br>年齡:<input type="text" name="stuAge"/><br><!-- 使用級聯屬性 -->家庭地址:<input type="text" name="address.homeAddress"/><br>學校地址:<input type="text" name="address.schoolAddress"/><br><input type="submit" value="提交"/></form>
請求處理類:FirstSpringDemo.java
// import…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{//使用實體類對象接收請求參數值(form表單中提交的數據)@RequestMapping("/entityProperties")public String entityProperties(Student student){System.out.println(student);return "success";}…}
執行index.jsp,如下

圖27-09
點擊提交后,控制台的輸出結果:

圖27-10
27.2.8 使用Servlet API作為參數
如果想使用原生的Servlet API進行開發,只需要將Servlet API放入方法的參數中,如下:
…//使用Servlet API開發@RequestMapping("/developWithServletAPI")public String developWithServletAPI(HttpServletRequest requst,HttpServletResponse response, HttpSession session){//使用request和response參數處理請求或響應...return "success";}…
27.3 處理模型數據
假設需要從數據庫查詢數據:在MVC設計模式中,用戶從視圖頁面(V)發起一個請求到控制器(C),控制器調用Service/Dao等處理數據,並從數據庫中返回數據(M)。之后,控制器(C)拿到數據(M)后加以處理,並返回到視圖頁面(V)。
SpringMVC提供了四種途徑來處理帶數據的視圖(M和V):ModelAndView,ModelMap、Map及Model,@SessionAttributes,@ModelAttribute
27.3.1 ModelAndView
ModelAndView包含了Model(M)和View(V)兩部分。用法如下:
index.jsp
…<a href="FirstSpringDemo/testModelAndView">testModelAndView</a>…
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testModelAndView")public ModelAndView testModelAndView(){String view = "success";ModelAndView mav= new ModelAndView(view);Student student = new Student("張三",23);//添加student對象數據放入ModelAndView中mav.addObject("student ",student);return mav;}…}
通過ModelAndView的構造方法將視圖頁面的名稱”success”放入mav對象,再通過addObject()方法將數據放入mav對象,最后返回mav。之后,程序就會跳轉到mav指定的視圖頁面views/success.jsp(仍然會被視圖解析器加上了前綴和后綴),並將mav中的數據student放入request作用於之中。
返回的視圖頁面:success.jsp
<body>${requestScope.student.stuName }</body>
執行index.jsp中的超鏈接,運行結果:

圖27-11
27.3.2 使用Map、ModelMap、Model作為方法的參數處理數據
可以給SpringMVC的請求處理方法增加一個Map類型的參數。如果向此Map中增加數據,那么該數據也會被放入request作用域中。
index.jsp
<a href="FirstSpringDemo/testMap">testMap</a>
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testMap")public String testMap(Map<String, Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";} …}
返回的視圖頁面success.jsp及運行結果同上例。
除了Map以外,還可以給請求處理方法增加一個ModelMap或Model類型的參數,效果完全一樣,如下:
使用ModelMap類型的參數
@RequestMapping("/testModelMap")public String testMap(ModelMap map){Student student = new Student("張三", 23);map.put("student", student);return "success";}
使用Model類型的參數
@RequestMapping("/testModel")public String testModel(Model map){Student student = new Student("張三", 23);map.addAttribute("student", student);return "success";}
27.3.3 使用@SessionAttributes處理數據
我們已經知道,向ModelAndView以及Map、ModelMap、Model參數中增加數據時,數據會同時被放到request作用域中。如果還要把數據放到session作用域中,就需要使用@SessionAttributes注解,如下:
index.jsp
<a href="FirstSpringDemo/testSessionAttribute">testSessionAttribute</a>
請求處理類:FirstSpringDemo.java
@SessionAttributes(value="student")@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testSessionAttribute")public String testSessionAttribute(Map<String ,Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";}…}
在類的上方加入@SessionAttributes(value="student"),表示將request作用域中的"student"對象同時加入到session作用域之中。
返回的視圖頁面:success.jsp
…<body>request作用域中:${requestScope.student.stuName } <br/>session作用域中:${sessionScope.student.stuName } <br/></body>…
執行index.jsp中的超鏈接,運行結果:

圖27-12
@SessionAttributes除了可以使用value將指定的對象名加入到session范圍,還可以使用types將某一個類型的對象都加入到session范圍,如下:
請求處理類:FirstSpringDemo.java
…@SessionAttributes(types=Student.class)@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testSessionAttribute")public String testSessionAttribute(Map<String ,Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";}…}
通過@SessionAttributes(types=Student.class) 將request作用域中Student類型的對象同時加入到session作用域之中。
27.3.4 使用@ModelAttribute注解處理數據
假設數據庫中存在一條學生信息,如下:

圖27-13
現在我們需要修改學生的年齡(姓名等其他信息不變),先嘗試用以下方式完成:
index.jsp(修改學號為31號的學生年齡)
…<form action="FirstSpringDemo/testModelAttribute" method="post"><input type="hidden" value="31" name="stuNo"/>年齡:<input type="text" name="stuAge"/><br><input type="submit" value="修改"/></form>…
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testModelAttribute")public String testModelAttribute(Student student){//省略數據庫的更新操作:將數據表中stuNo=31的學生信息,更新為參數student中的各屬性值System.out.println("更新后的學生信息:姓名: "+ student.getStuName()+",年齡:"+student.getStuAge());return "success";}}…
執行index.jsp,並將年齡修改為66歲,如圖:

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

圖27-15
可以發現,年齡確實成功修改了,但是姓名卻變成了null。這是因為在index.jsp的form表單中,只提交了stuAge字段的屬性,而不存在stuName等其他字段的屬性,因此stuAge屬性會從輸入框中獲取,而其他屬性值會使用相應類型的默認值(如String類型的stuName默認值就是null)。
這與我們的本意不符,我們的本意是:被修改的屬性,使用修改后的值(如stuAge);而沒被修改的屬性,要使用數據庫中原有的值(如果stuName應該保留“張三”)。要想實現我們的“本意”,可以在請求控制類中增加一個用@ModelAttribute的方法,如下:
請求處理類:FirstSpringDemo.java
…//@SessionAttributes(types=Student.class)@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@ModelAttributepublic void queryStudentBeforeUpdate (int stuNo,Map<String,Object> map){//使用帶數據的實體類對象,模擬從數據庫中獲取學號為stuNo的學生對象Student student = new Student();student.setStuNo(stuNo);student.setStuName("張三");student.setStuAge(23);//即用以上語句模擬Student student= stuService.queryStudentByNo(stuNo);//將從數據庫中查詢的student對象放入map中map.put("student", student);}@RequestMapping("/testModelAttribute")public String testModelAttribute(Student student){//省略數據庫的更新操作System.out.println("更新后的學生信息:姓名: " + student.getStuName()+",年齡:"+student.getStuAge());return "success";}}
重新提交之前的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表單傳來的stuNo和stuAge屬性得到了修改,而form表單中不存在的stuName屬性則保持不變。如果map中的key值與請求處理方法的參數名不一致,則需要在參數前使用@ModelAttribute標識出map中對應的key值,如下:
請求處理類:FirstSpringDemo.java
…@ModelAttributepublic void queryStudentBeforeUpdate(int stuNo,Map<String,Object> map){...map.put("stu", student);}@RequestMapping("/testModelAttribute")public String testModelAttribute(@ModelAttribute("stu")Student student){...return "success";}…
map中的key是”stu”,與方法的參數名”student”不一致,就需要在參數名前使用@ModelAttribute("stu")來進行標識。
①參數名:實際是判斷是否與“首字母小寫的參數類型”一致。如參數的類型是Student,則會判斷是否與首字母小寫的參數類型(即”student”)一致。此段落中,用“參數名”來代替“首字母小寫的參數類型”僅僅是為了便於讀者閱讀。
說明:
標有@ModelAttribute注解的方法,會在請求處理類中的每一個方法執行前,都執行一次,因此需要謹慎使用。
27.1.1 @RequestMapping使用
之前,我們是把@RequestMapping注解放在方法之上,用來給方法綁定一個請求映射。除此以外,@RequestMapping注解還可以放在類的上面。例如,我們給之前的請求處理類(FirstSpringDemo.java)的類名上也加一個@RequestMapping注解,如下,
FirstSpringDemo.java
…@Controller@RequestMapping("/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/firstSpringMVC")public String welcomeToSpringMVC(){System.out.println("welcome to springMVC");return "success";}}
類前面加了@RequestMapping注解以后,前端發來的請求就不能再直接去匹配方法上面的@RequestMapping了。而是應該先匹配類前的@RequestMapping值,再匹配方法前的@RequestMapping。
因此,方法前面@RequestMapping的值,是相對於類前的@RequestMapping值。如果類的前面不存在@RequestMapping,則方法前的@RequestMapping值就是相對於項目根目錄。 例如,在類前加了@ RequestMapping("/FirstSpringDemo")以后,前台就必須通過以下路徑來訪問:
index.jsp
…<body><a href="FirstSpringDemo/firstSpringMVC">My First SpringMVC Demo</a></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
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/firstSpringMVC",method=RequestMethod.POST)public String welcomeToSpringMVC(){return "success";}}…
如果再點擊上面超鏈接,運行結果如下,

圖27-01
提示我們“請求方法不支持GET方式”。
如果把超鏈接,替換成以下POST方式的表單提交,
index.jsp
…<body><form action="FirstSpringDemo/firstSpringMVC" method="POST"><input type="submit" value="POST方式提交"/></form></body>…
點擊“POST方式提交”后,就又會正常運行。
(2)params屬性
例如,我們通過超鏈接加入兩個請求參數,如下,
index.jsp
…<body><a href="FirstSpringDemo/requestWithParams?name=zhangsan&age=20">requestWithParams...</a></body>…
再通過params來檢查請求中的參數是否符合要求,如下,
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithParams",params={"name","age!=23"})public String requestWithParams(){return "success";}}…
以上請求通過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
…@RequestMapping(value="/requestWithHeaders",headers={"Accept-Language=zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3","Accept-Encoding=gzip, deflate"})public String requestWithHeaders(){return "success";}…
關於“請求頭”的知識,讀者可以查閱相關資料,本書不作為重點講解。
27.2 基於@RequestMapping的配置
27.2.1 Ant風格的請求路徑
SpringMVC除了支持傳統方式的請求外,還支持Ant風格的請求路徑。
Ant風格的請求路徑支持以下3種通配符:
| 通配符 | 簡介 |
| ? | 匹配任何單字符 |
| * | 匹配0或者任意數量的字符 |
| ** | 匹配0或者更多的目錄 |
例如,在處理方法前配置@RequestMapping(value="/requestWithAntPath/*/test"),表示請求路徑的“requestWithAntPath”和“test”之間可以填任意字符,如下,
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithAntPath/*/test")public String requestWithAntPath(){return "success";}…}
如果前端發送以下請求,是可以匹配到requestWithAntPath()方法的。
index.jsp
…<body><a href="FirstSpringDemo/requestWithAntPath/lanqiao/test">requestWithAntPath...</a>…</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
…<body><a href="FirstSpringDemo/requestWithPathVariable/9527">requestWithPathVariable...</a></body>…
處理方法就可以通過@PathVariable來獲取此參數值,如下
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping(value="/requestWithPathVariable/{id}")public StringrequestWithPathVariable(@PathVariable("id") Integer id){System.out.println("id:"+id);return "success";}}…
具體是通過@RequestMapping(value="/requestWithPathVariable/{id}")中的占位符“{id}”接收到參數值“9527”,再把參數值傳給@PathVariable("id")中的“id”,最后再把值賦值給方法的參數id。
27.2.3 REST風格
REST(Representational State Transfer)是一種編程風格,可以顯著降低開發的復雜性,是當前非常流行的一種互聯網軟件架構。
在學習REST之前,我們首先要知道,在HTTP協議里面有多種請求方式,並且其中的POST、DELETE、PUT、GET等四個方式,分別對應增刪改查四種操作,具體是:POST對應“增”,DELETE對應“刪”、PUT對應“改”,GET對應“查”。但是普通瀏覽器中的form表單,只支持GET和POST兩種請求方式。為了使普通瀏覽器支持PUT和DELETE方式,可以使用Spring提供的過濾器HiddenHttpMethodFilter,此過濾器可以通過一定的規則,將部分POST請求轉換為PUT或DELETE請求。如果讀者想了解HiddenHttpMethodFilter的底層代碼,可以閱讀spring-web-x.x.xRELEASE.jar包中HiddenHttpMethodFilter類里的doFilterInternal()方法。
實現PUT或DELETE請求方式的步驟如下:
1.在web.xml中配置HiddenHttpMethodFilter過濾器,如下,
web.xml
…<web-app …><filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
2.在form表單中指定請求方式為method="post";並在表單中增加一個hidden隱藏域,且設置隱藏域的name及value屬性:name="_method"、value="PUT"或value="DELETE"。
3.在處理方法的@RequestMapping注解中,用method屬性指定請求方式(如method=RequestMethod.DELETE、method=RequestMethod.PUT等)。
例如,在web.xml中配置了HiddenHttpMethodFilter以后,就可以使用下面的方式發送並處理增、刪、改、查的請求:
①發送請求
index.jsp
…<body>…<form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="hidden" name="_method" value="DELETE" /><input type="submit" value="刪除" /></form><form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="hidden" name="_method" value="PUT" /><input type="submit" value="修改" /></form><form action="FirstSpringDemo/requestWithREST/9527"method="post"><input type="submit" value="增加" /></form><a href="FirstSpringDemo/requestWithREST/9527">查看</a></body>…
②處理請求
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{…//使用REST風格,處理“刪除”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.DELETE)public String requestWithRestDelete(@PathVariable("id")Integer id){System.out.println("刪除時需要的id:"+id);return "success";}//使用REST風格,處理“修改”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.PUT)public String requestWithRestPut(@PathVariable("id")Integer id){System.out.println("修改時需要的id:"+id);return "success";}//使用REST風格,處理“增加”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.POST)public String requestWithRestAdd(@PathVariable("id")Integer id){System.out.println("增加時需要的id:"+id);return "success";}//使用REST風格,處理“查看”的請求@RequestMapping(value="/requestWithREST/{id}",method=RequestMethod.GET)public String requestWithRestGet(@PathVariable("id") Integer id){System.out.println("查詢時需要的id:"+id);return "success";}}…
運行index.jsp頁面,如圖,

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

圖27-04
27.2.4 使用@RequestParam獲取請求參數
Spring MVC可以通過@RequestParam來接收請求中的參數值,該注解有三個常用的屬性:
| 屬性名 | 簡介 |
| value | 請求攜帶參數的參數名 |
| required | 標識請求參數中是否必須存在某個具體的參數。 true(默認):必須存在;若不存在,將拋出異常。 false:不必須。 |
| defaultValue | 給參數賦一個默認值。如果請求中不存在某個參數,則該參數就取defaultValue所設置的值。 |
index.jsp
…<a href="FirstSpringDemo/requestParam?name=zhangsan&age=23">TestRequestParam</a>…
FirstSpringDemo.java
…@Controller@RequestMapping(value="/FirstSpringDemo")public class FirstSpringDemo{//使用@RequestParam注解接收請求參數@RequestMapping("/requestParam")public String requestParam(@RequestParam(value="name") String name, @RequestParam(value="age") Integer age){System.out.println("name: " + name + " age: " + age);return "success";}}…
@RequestParam通過value值與傳入的參數名匹配,並將參數值賦值給@RequestParam后面的變量。例如,通過@RequestParam(value="name")接收index.jsp傳來的name參數值(即zhangsan),並將參數值(zhangsan)賦值給@RequestParam后面的String name,類似於String name="zhangsan"。
若將請求中的age參數刪除,如下
index.jsp
<a href="FirstSpringDemo/requestParam?name=zhangsan">TestRequestParam</a>
再次執行以上超鏈接,則會發生異常,如下:

圖27-05
為了解決此異常,就可以給age參數的@RequestParam加入required=false,如下:
FirstSpringDemo.java
…@RequestMapping("/requestParam")public String requestParam(@RequestParam(value = "name") String name, @RequestParam(value = "age",required=false) Integer age){…}…
此外,還可以通過@RequestParam的defaultValue屬性給請求參數設置默認值,如下,
FirstSpringDemo.java
…@RequestMapping("/requestParam")public String requestParam(@RequestParam(value = "name")String name, @RequestParam(value = "age",required=false,defaultValue="23") Integer age){System.out.println("name: " + name + " age: " + age);return "success";}…
通過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
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/requestHeader")public StringrequestHeader(@RequestHeader(value="Accept-Language")String al){System.out.println("Accept-Language:" + al);return "success";}}…
通過@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
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/cookieValue")public String cookieValue(@CookieValue(value="JSESSIONID")String sessionid){System.out.println("sessionid:" + sessionid);return "success";}}…
運行結果:

圖27-08
27.2.7 使用實體類對象接收請求參數值
如果處理方法的參數是一個實體類對象,那么SpringMVC會將請求的參數名與實體類對象的屬性進行匹配,為實體類對象的屬性賦值,並且支持級聯屬性的賦值。以下是具體的示例:
實體類:Student.java
public class Student{private String stuName;private int stuAge;private Address address ;//setter、getter@Overridepublic String toString(){return "姓名:"+this.stuName+"\t年齡:"+this.stuAge+"\t家庭地址:"+this.address.getHomeAddress()+"\t學校地址:"+this.address.getSchoolAddress();}}
實體類:Address.java
public class Address{private String schoolAddress;private String homeAddress;//setter、getter}
在form表單中使用實體類的屬性名作為<input>標簽的name值(可使用級聯屬性):
請求頁:index.jsp
<form action="FirstSpringDemo/entityProperties">姓名:<input type="text" name="stuName"/><br>年齡:<input type="text" name="stuAge"/><br><!-- 使用級聯屬性 -->家庭地址:<input type="text" name="address.homeAddress"/><br>學校地址:<input type="text" name="address.schoolAddress"/><br><input type="submit" value="提交"/></form>
請求處理類:FirstSpringDemo.java
// import…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{//使用實體類對象接收請求參數值(form表單中提交的數據)@RequestMapping("/entityProperties")public String entityProperties(Student student){System.out.println(student);return "success";}…}
執行index.jsp,如下

圖27-09
點擊提交后,控制台的輸出結果:

圖27-10
27.2.8 使用Servlet API作為參數
如果想使用原生的Servlet API進行開發,只需要將Servlet API放入方法的參數中,如下:
…//使用Servlet API開發@RequestMapping("/developWithServletAPI")public String developWithServletAPI(HttpServletRequest requst,HttpServletResponse response, HttpSession session){//使用request和response參數處理請求或響應...return "success";}…
27.3 處理模型數據
假設需要從數據庫查詢數據:在MVC設計模式中,用戶從視圖頁面(V)發起一個請求到控制器(C),控制器調用Service/Dao等處理數據,並從數據庫中返回數據(M)。之后,控制器(C)拿到數據(M)后加以處理,並返回到視圖頁面(V)。
SpringMVC提供了四種途徑來處理帶數據的視圖(M和V):ModelAndView,ModelMap、Map及Model,@SessionAttributes,@ModelAttribute
27.3.1 ModelAndView
ModelAndView包含了Model(M)和View(V)兩部分。用法如下:
index.jsp
…<a href="FirstSpringDemo/testModelAndView">testModelAndView</a>…
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testModelAndView")public ModelAndView testModelAndView(){String view = "success";ModelAndView mav= new ModelAndView(view);Student student = new Student("張三",23);//添加student對象數據放入ModelAndView中mav.addObject("student ",student);return mav;}…}
通過ModelAndView的構造方法將視圖頁面的名稱”success”放入mav對象,再通過addObject()方法將數據放入mav對象,最后返回mav。之后,程序就會跳轉到mav指定的視圖頁面views/success.jsp(仍然會被視圖解析器加上了前綴和后綴),並將mav中的數據student放入request作用於之中。
返回的視圖頁面:success.jsp
<body>${requestScope.student.stuName }</body>
執行index.jsp中的超鏈接,運行結果:

圖27-11
27.3.2 使用Map、ModelMap、Model作為方法的參數處理數據
可以給SpringMVC的請求處理方法增加一個Map類型的參數。如果向此Map中增加數據,那么該數據也會被放入request作用域中。
index.jsp
<a href="FirstSpringDemo/testMap">testMap</a>
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testMap")public String testMap(Map<String, Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";} …}
返回的視圖頁面success.jsp及運行結果同上例。
除了Map以外,還可以給請求處理方法增加一個ModelMap或Model類型的參數,效果完全一樣,如下:
使用ModelMap類型的參數
@RequestMapping("/testModelMap")public String testMap(ModelMap map){Student student = new Student("張三", 23);map.put("student", student);return "success";}
使用Model類型的參數
@RequestMapping("/testModel")public String testModel(Model map){Student student = new Student("張三", 23);map.addAttribute("student", student);return "success";}
27.3.3 使用@SessionAttributes處理數據
我們已經知道,向ModelAndView以及Map、ModelMap、Model參數中增加數據時,數據會同時被放到request作用域中。如果還要把數據放到session作用域中,就需要使用@SessionAttributes注解,如下:
index.jsp
<a href="FirstSpringDemo/testSessionAttribute">testSessionAttribute</a>
請求處理類:FirstSpringDemo.java
@SessionAttributes(value="student")@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testSessionAttribute")public String testSessionAttribute(Map<String ,Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";}…}
在類的上方加入@SessionAttributes(value="student"),表示將request作用域中的"student"對象同時加入到session作用域之中。
返回的視圖頁面:success.jsp
…<body>request作用域中:${requestScope.student.stuName } <br/>session作用域中:${sessionScope.student.stuName } <br/></body>…
執行index.jsp中的超鏈接,運行結果:

圖27-12
@SessionAttributes除了可以使用value將指定的對象名加入到session范圍,還可以使用types將某一個類型的對象都加入到session范圍,如下:
請求處理類:FirstSpringDemo.java
…@SessionAttributes(types=Student.class)@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testSessionAttribute")public String testSessionAttribute(Map<String ,Object> map){Student student = new Student("張三", 23);map.put("student", student);return "success";}…}
通過@SessionAttributes(types=Student.class) 將request作用域中Student類型的對象同時加入到session作用域之中。
27.3.4 使用@ModelAttribute注解處理數據
假設數據庫中存在一條學生信息,如下:

圖27-13
現在我們需要修改學生的年齡(姓名等其他信息不變),先嘗試用以下方式完成:
index.jsp(修改學號為31號的學生年齡)
…<form action="FirstSpringDemo/testModelAttribute" method="post"><input type="hidden" value="31" name="stuNo"/>年齡:<input type="text" name="stuAge"/><br><input type="submit" value="修改"/></form>…
請求處理類:FirstSpringDemo.java
…@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@RequestMapping("/testModelAttribute")public String testModelAttribute(Student student){//省略數據庫的更新操作:將數據表中stuNo=31的學生信息,更新為參數student中的各屬性值System.out.println("更新后的學生信息:姓名: "+ student.getStuName()+",年齡:"+student.getStuAge());return "success";}}…
執行index.jsp,並將年齡修改為66歲,如圖:

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

圖27-15
可以發現,年齡確實成功修改了,但是姓名卻變成了null。這是因為在index.jsp的form表單中,只提交了stuAge字段的屬性,而不存在stuName等其他字段的屬性,因此stuAge屬性會從輸入框中獲取,而其他屬性值會使用相應類型的默認值(如String類型的stuName默認值就是null)。
這與我們的本意不符,我們的本意是:被修改的屬性,使用修改后的值(如stuAge);而沒被修改的屬性,要使用數據庫中原有的值(如果stuName應該保留“張三”)。要想實現我們的“本意”,可以在請求控制類中增加一個用@ModelAttribute的方法,如下:
請求處理類:FirstSpringDemo.java
…//@SessionAttributes(types=Student.class)@Controller@RequestMapping(value = "/FirstSpringDemo")public class FirstSpringDemo{@ModelAttributepublic void queryStudentBeforeUpdate (int stuNo,Map<String,Object> map){//使用帶數據的實體類對象,模擬從數據庫中獲取學號為stuNo的學生對象Student student = new Student();student.setStuNo(stuNo);student.setStuName("張三");student.setStuAge(23);//即用以上語句模擬Student student= stuService.queryStudentByNo(stuNo);//將從數據庫中查詢的student對象放入map中map.put("student", student);}@RequestMapping("/testModelAttribute")public String testModelAttribute(Student student){//省略數據庫的更新操作System.out.println("更新后的學生信息:姓名: " + student.getStuName()+",年齡:"+student.getStuAge());return "success";}}
重新提交之前的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表單傳來的stuNo和stuAge屬性得到了修改,而form表單中不存在的stuName屬性則保持不變。如果map中的key值與請求處理方法的參數名不一致,則需要在參數前使用@ModelAttribute標識出map中對應的key值,如下:
請求處理類:FirstSpringDemo.java
…@ModelAttributepublic void queryStudentBeforeUpdate(int stuNo,Map<String,Object> map){...map.put("stu", student);}@RequestMapping("/testModelAttribute")public String testModelAttribute(@ModelAttribute("stu")Student student){...return "success";}…
map中的key是”stu”,與方法的參數名”student”不一致,就需要在參數名前使用@ModelAttribute("stu")來進行標識。
①參數名:實際是判斷是否與“首字母小寫的參數類型”一致。如參數的類型是Student,則會判斷是否與首字母小寫的參數類型(即”student”)一致。此段落中,用“參數名”來代替“首字母小寫的參數類型”僅僅是為了便於讀者閱讀。
說明:
標有@ModelAttribute注解的方法,會在請求處理類中的每一個方法執行前,都執行一次,因此需要謹慎使用。
