zoukankan      html  css  js  c++  java
  • 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+" 年龄:"+this.stuAge
    11. +" 家庭地址:"+this.address.getHomeAddress()
    12. +" 学校地址:"+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+" 年龄:"+this.stuAge
    11. +" 家庭地址:"+this.address.getHomeAddress()
    12. +" 学校地址:"+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注解的方法,会在请求处理类中的每一个方法执行前,都执行一次,因此需要谨慎使用。

  • 相关阅读:
    常用网站
    我的第一个 python 爬虫脚本
    在文件夹下所有文件中查找字符串(linux/windows)
    Python 列表 insert() 方法
    mysql 替换 tab 键 ( )
    访问权限的修饰符
    eclipse 快捷键
    位运算
    hadoop 环境搭建
    Hadoop 快速入门
  • 原文地址:https://www.cnblogs.com/gu-bin/p/10532890.html
Copyright © 2011-2022 走看看