vlambda博客
学习文章列表

(六)Spring MVC常用注解(下)

6.2  参数绑定注解

6.2.1  @RequstParam注解

@RequstParam注解用于将制定的请求参数赋值给方法中的形参。@RequstParam注解可以使用的属性如表6-5所示。

6-5  @RequstParam常用属性

属性名称

    

是否必填

    

name

String

指定请求头绑定的名称

value

String

name属性的别名

required

Boolean

指定参数是否必须绑定

defaultValue

String

请求没有传递参数而使用的默认值

@RequstParam注解的使用实例如下:

@Controller@RequestMapping(value = "/user")public class AyUserController {  @RequestMapping("findById") public String findById(@RequestParam(value="id") String id){ AyUser ayUser = ayUserService.findById(id); return "success"; }}


当浏览器中输入URL 请求http://localhost:8080/user/findById?id=1 时,请求中的id=1 会将值赋给findById 方法中的id变量。@R equstParam 注解可以使用required属性指定参数是否必须传值。如果参数没有接收到任何值,可以使用defaultValue 指定参数的默认值。具体代码如下 所示:
@Controller@RequestMapping(value = "/user")public class AyUserController {  @RequestMapping("/findByNameAndPassword") public String findByNameAndPassword( @RequestParam(value="name") String name, @RequestParam(value="password",required = false,defaultValue = "123") String password){ System.out.println("name=" + name); System.out.println("password" + password); return "success"; }}


当浏览器中输入URL请求:http://localhost:8080/user/findByNameAndPassword?name=ay时,name参数被赋值为ay,由于password没传任何值,故默认值为123


6.2.2  @PathVariable注解

@PathVariable 注解可以将URL中动态参数绑定到控制器处理方法的入参中。@PathVariable 注解只有一个value属性,类型为String ,表示绑定的名称,如果缺省则默认绑定同名参数。具体示例如下所示:
@Controller@RequestMapping(value = "/user")public class AyUserController {  @RequestMapping("/owners/{ownerId}/pets/{petId}") public String findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... return ""; }}


当在浏览器中输入请求URL http://localhost:8080/user/owners/123/pets/456 ,则自动将动态参数{ownerId} {petId} 的值123 456 绑定到@PathVariable 注解的同名参数上,即ownerI d  = 123 petI d  = 456
URL 中的动态参数除了可以绑定到方法上,还可以绑定到类上,具体示例如下所示:
@Controller@RequestMapping(value = "/owners/{ownerId}")public class AyUserController {  @RequestMapping("/pets/{petId}") public String findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... return ""; }}


当在浏览器中输入请求URL http://localhost:8080/owners/123/pets/456 时,则自动将类上的动态参数{ownerId} 和方法上的动态参数{petId} 的值绑定到@PathVariable 注解的同名参数上,即ownerI d  = 123 petI d  = 456

6.2.3  @RequestHeader注解

@RequestHeader 注解可以将请求的头信息数据映射到处理的方法参数上 @RequestHeader 注解的属性如表6-6 所示。

6-6  @RequestHeader常用属性

属性名称

    

是否必填

    

name

String

指定请求头绑定的名称

value

String

name属性的别名

required

Boolean

指定参数是否必须绑定

defaultValue

String

请求没有传递参数而使用的默认值

一般请求头信息如下所示:

Host localhost:8080Accepttext/html,application/xhtml+xml,application/xml;q=0.9Accept-Languagefr,en-gb;q=0.7,en;q=0.3Accept-Encodinggzip,deflateAccept-CharsetISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive300

下面通过@RequestHeader注解获取Accept-EncodingKeep-Alive信息,具体示例如下所示:

@Controller@RequestMapping(value = "/user")public class AyUserController {  @RequestMapping("/requestHeader") public String handle( @RequestHeader("Accept-Encoding") String[] encoding, @RequestHeader("Accept") String[] accept) { //... return ""; } }


当在浏览器中输入请求URL http://localhost:8080/user/requestHeader 时,则自动将请求头Accept-Encoding ”和“Accept ”的值赋到encoding accept 变量上,由于请求头Accept-Encoding ”和“Accept ”的数据类型是数组,所有定义encoding accept 变量为String[] 类型。

6.2.4  @CookieValue注解

@CookieValue 注解用于将请求的Cookie 信息映射到处理的方法参数上 @CookieValue 注解的属性如表6-7 所示。

6-7  @CookieValue 常用属性

属性名称

    

是否必填

    

name

String

指定请求头绑定的名称

value

String

name属性的别名

required

Boolean

指定参数是否必须绑定

defaultValue

String

请求没有传递参数而使用的默认值

@CookieValue 注解如何使用,具体示例如下所示:
@Controller@RequestMapping(value = "/user")public class AyUserController {  @RequestMapping("/cookieValue") public String handle(@CookieValue("JSESSIONID") String cookie) { //... return ""; }}


当在浏览器中输入请求URLhttp://localhost:8080/user/cookieValue 时,则自动将JSESSIONID 值设置到Cookie 参数上。

6.2.5  @ModelAttribute注解

@ModelAttribute 注解主要是将请求参数绑定到Model对象 @ModelAttribute 注解只有一个value属性,类型为String,表示绑定的属性名称。当Controller类中有任意一个方法被@ModelAttribute注解标记,页面请求只要进入这个控制器,不管请求那个方法,均会先执行被@ModelAttribute标记的方法,所以可以用@ModelAttribute注解的方法做一些初始化操作。当同一个C ontroller类中有多个方法被@ModelAttribute注解标记,所有被@ModelAttribute标记的方法均会被执行,按先后顺序执行,然后再进入请求的方法。具体实例代码如下所示:
@Controller@RequestMapping(value = "/user")public class AyUserController {  @ModelAttribute public void init(){ System.out.println("init ..."); }  @ModelAttribute public void init02(){ System.out.println("init 02 ..."); }  @GetMapping("/findById/{id}") public String findById(@PathVariable String id) { // ... return ""; }  @ModelAttribute public void init03(){ System.out.println("init 03 ..."); }}


当在浏览器输入访问URL http://localhost:8080/user/findById/1 时,便可以在控制台看到打印信息:
init ...init 02 ...init 03 ...

@ModelAttribute注解有很多的额使用方式,下面逐一进行介绍。

1. @ModelAtterbute方法无返回值情况

@Controller@RequestMapping(value = "/user")public class AyUserController {  @ModelAttribute public void init(Model model){ AyUser ayUser = new AyUser(); ayUser.setId(1); ayUser.setName("ay"); model.addAttribute("user", ayUser); }  @GetMapping("/hello") public String hello(){ return "hello"; }}


上述代码中@ModelAttribute 注解标记的init方法无任何返回值,在init方法中创建一个用户对象AyU ser并设置idname的值,最后调用Model对象的addAttribute 方法设置到Model对象中。对应前端src\main\webapp\WEB-INF\views\hello.jsp页面代码如下所示:
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%><!DOCTYPE HTML><html><head> <title>Getting Started: Serving Web Content</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>hello, ${user.name}</body></html>
当在浏览器中输入请求URL http://localhost:8080/user/hello 时,浏览器显示helloay”。
从执行结果可以看出,当执行请求时,首先访问init方法,然后再访问hello方法,并且是同一个请求。因为model模型数据的作用域与request相同,所以可以用@ModelAttribute 注解直接标记在方法上对实际要访问的方法进行一些初始化操作。

2. @ModelAttribute标记方法有返回值

@Controller@RequestMapping(value = "/user")public class AyUserController {  @ModelAttribute("name") public String init(@RequestParam(value = "name", required = false) String name){ return name; }  @GetMapping("/hello") public String hello(){ return "hello"; }}


上述代码中,@ModelAttribute 注解标注的init方法带有返回值,@ModelAttribute("name") value属性值为key,而init方法值为value,类似于:
model.addAttribute("name",name);对应前端src\main\webapp\WEB-INF\views\hello.jsp页面代码如下所示:<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%><!DOCTYPE HTML><html><head> <title>Getting Started: Serving Web Content</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>hello, ${name}</body></html>
当在浏览器中输入请求URLhttp://localhost:8080/user/hello时,浏览器显示“helloay”。

3. @ModelAttribute注解和@RequestMapping注解

@ModelAttribute注解和@RequestMapping注解同时标记在一个方法上。

@Controllerpublic class AyUserController {  @ModelAttribute("name") @RequestMapping(value = "/hello") public String hello(){ return "ay"; }}


上述代码中,@ModelAttribute注解和@RequestMapping注解同时标记在hello方法上,同时我们把AyUserController 类上的@RequestMapping注解去掉。此时,hello方法的返回值“ay”并不是视图名称,而是model属性的值,视图名称是@RequestMapping value值“hello”。Model的属性名称由@ModelAttribute value值指定,相当于在request中封装了namekey=  ayvalue)。对应前端src\main\webapp\WEB-INF\views\hello.jsp页面代码如下所示:
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%><!DOCTYPE HTML><html><head> <title>Getting Started: Serving Web Content</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>hello, ${name}</body></html>
当在浏览器中输入请求 URL http://localhost:8080/hello 时,浏览器显示“ hello ay ”。

4. 使用@ModelAttribute注解方法的参数。

@Controller@RequestMapping("/user")public class AyUserController {  @ModelAttribute("ayUser") public AyUser init(@RequestParam("id") Integer id, @RequestParam("name") String name){ AyUser ayUser = new AyUser(); ayUser.setId(id); ayUser.setName(name); return ayUser; }  @RequestMapping(value="hello") public String hello(@ModelAttribute("ayUser") AyUser ayUser){  return "hello"; }



当在浏览器中输入请求URLhttp://localhost:8080/user/hello?id=1&name=ay 时,会优先执行init方法, 把idname的值赋值给in i t方法绑定的参数,同时构造A yU ser对象返回。这里model的属性名称就是@ModelAttribute("ayUser") value值“ayU ser”,model的属性值就是init方法的返回值。h ello方法的参数AyUser 使用了注解@ModelAttribute("ayUser") ,表示参数ayU ser的值就是init方法中的model属性。
对应前端src\main\webapp\WEB-INF\views\hello.jsp页面代码如下所示:
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%><!DOCTYPE HTML><html><head> <title>Getting Started: Serving Web Content</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>hello, ${ayUser.name}</body></html>


6.2.6  @SessionAttribute@SessionAttributes注解

@ModelAttribute注解作用在方法或者方法的参数上,表示将被注解的方法的返回值或者是被注解的参数作为Model的属性加入到Model中,Spring框架会将Model传递给前端。Model的生命周期只存在 HTTP 请求的处理过程 ,请求处理完成后,Model就销毁了。如果想让参数在多个请求间共享,那么需要用到@SessionAttributes注解。@SessionAttributes注解只能声明在类上,不能声明在方法上。
@SessionAttributes 注解常用的属性如表6-8 所示。

6-8  @RequestHeader常用属性

属性名称

    

是否必填

    

names

String[]

需要存储到session中数据的名称

value

String

name属性的别名

types

Class<?>[]

根据指定参数的类型,将模型中对应类型的参数存储到session

下面看具体实例,具体代码如下:

@Controller@SessionAttributes("ayUser")@RequestMapping("/user")public class AyUserController {  @RequestMapping("redirect") public String redirectTest(Model model){ AyUser ayUser = new AyUser(); ayUser.setName("ay"); model.addAttribute("ayUser",ayUser); return "redirect:hello"; }  @RequestMapping("hello") public String hello(ModelMap modelMap){ AyUser ayUser = (AyUser) modelMap.get("ayUser"); System.out.println(ayUser.getName()); return "hello"; }}


在浏览器中输入请求URL http://localhost:8080/user/redirect 时,由于AyUserController 类上添加注解@SessionAttributes("ayUser") ,方法redirectTest 在执行过程中,将AyUser 对象存放到M odel对象的同时,也会把对象存放到HttpS ession作用域中。redirectTest 方法执行完成之后,会重定向到hello方法。在hello方法中,HttpS ession对象会将@SessionAttributes注解的属性写入到新的Model中,所以可以通过ModelMap 获取的AyU ser对象打印信息。
除了使用ModelMap 来接收HttpSession 对象中的值外,还可以使用@SessionAttribute 注解,具体实例如下所示:
@Controller@SessionAttributes("ayUser")@RequestMapping("/user")public class AyUserController {  @RequestMapping("redirect") public String redirectTest(Model model){ AyUser ayUser = new AyUser(); ayUser.setName("ay"); model.addAttribute("ayUser",ayUser); return "redirect:hello"; }  @RequestMapping("hello") public String hello(@SessionAttribute AyUser ayUser){ System.out.println(ayUser.getName()); return "hello"; }}


上述代码中,在hello方法中使用@SessionAttribute 注解来获取@SessionAttributes("ayUser") 注解中的ayU ser”对象并赋值给AyU ser对象,这样就可以很方便 hello方法中使用A yU ser对象。
如果想删除HttpSession 对象中共享的属性,可以通过SessionStatus.setComplete(),这句只会删除通过@SessionAttribute保存到HttpSession 中的属性。具体实例如下所示:
@RequestMapping("redirect")public String redirectTest(Model model,SessionStatus sessionStatus){ AyUser ayUser = new AyUser(); ayUser.setName("ay"); model.addAttribute("ayUser",ayUser); //删除HttpSession中的属性 sessionStatus.setComplete(); return "redirect:hello";}
还可以设置多个对象到HttpSession中,具体代码如下:
@SessionAttributes(types = {AyUser.class, AyRole.class},value = {"ayUser", "ayRole"})

type属性用来指定放入HttpSession当中的对象类型。


6.2.7  @ResponseBody@RequestBody注解

1. @ResponseBody注解

@ResponseBody 注解用于将Controller方法返回的对象,通过HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。@Responsebody注解将方法的返回结果直接写入HTTP响应正文(ResponseBody)中,一般在异步获取数据时使用。在使用@RequestMapping 注解时,返回值通常 解析为跳转路径, 加上@Responsebody后返回结果 不会被解析为跳转路径,而是直接写入 HTTP响应正文中。
使用@ResponseBody @RequestBody 注解之前,需要在pom.xml 文件中引入Jackson相关的依赖包,具体 示例 代码如下:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.5</version></dependency><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.5</version></dependency><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version></dependency>
S pring MVC 主要是利用类型转换器messageC onverters将前台信息转换为开发者需要的格式。然后在相应的Controller方法接受参数前添加@R equestB ody注解,进行数据转换,或者在方法的返回值类型处添加@ResponseBody 注解,将返回信息转换成相关格式的数据。
下面看具体 例,代码如下所示:

1)返回普通的字符串

@Controller@RequestMapping("/user")public class AyUserController {  @RequestMapping("/hello") @ResponseBody public String hello(){ return "I am not view"; }}


在浏览器 输入访问路径:http://localhost:8080/user/hello ,方法返回的不是视图,而是把字符串I am not view ”直接写入HTTP 响应正文中,返回给浏览器。

2)返回集合对象

@Controller@RequestMapping("/user")public class AyUserController {  @RequestMapping("/hello") @ResponseBody public List<String> hello(){ List<String> list = new ArrayList<String>(); list.add("ay"); list.add("al"); return list; }}


在浏览器输入访问路径:http://localhost:8080/user/hello,方法返回的不是视图,而是把JSON 字符串{"name":"ay","age":"3"} ” 直接写入HTTP 响应正文中,返回给浏览器。
2. @RequestBody 注解
@RequestBody 注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到C ontroller方法的参数上。
下面看具体实例:
@Controller@RequestMapping("/user")public class AyUserController {  @RequestMapping("/hello") @ResponseBody public void hello(@RequestBody AyUser ayUser){ System.out.println("name" + ayUser.getName()); System.out.println("password" + ayUser.getPassword()); }}



6.3 信息转换详解

6.3.1  HttpMessageConverter<T>

S pring  MVC中,HttpMessageConverter接口扮演着重要的角色。Spring MVC 可以接收不同的消息格式,也可以将不同的消息格式响应回去 最常见的是JSON 。这些消息所蕴含的“有效信息”是一致的,那么各种不同的消息转换器,都会生成同样的转换结果。至于各种消息间解析细节的不同,就被屏蔽在不同的HttpMessageConverter实现类中了。HttpMessageConverter接口类具体的源码如下所示:
public interface HttpMessageConverter<T> { boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);  boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); List<MediaType> getSupportedMediaTypes();  T read(Class<? extends T> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;   void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException; }
canRead(Class<?> clazz, @Nullable MediaType mediaType) 指定转换器可以读取的对象类型,同时指定支持MIME类型(text /xmlapplication/json )。
canWrite(Class<?> clazz, @Nullable MediaType mediaType) 指定转换器可以将clazz类型的对象写到响应流中,响应流支持类型在m ediaType中定义。
getSupportedMediaTypes() 该转换器支持的媒体类型。
T read(Class<? Extends T> clazz, HttpInputMessage inputMessage) 将请求信息流转换成T类型的对象。
write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) T类型的对象写到响应流中,同时指定响应媒体类型为contentType
S pring MVC HttpMessageConverter接口提供了很多实现类,下面简单列举几个实现类的内容和作用,具体如表6-9 所示。

6-9  HttpMessageConverter接口实现类

属性名称

    

StringHttpMessageConverter

将请求信息转换为字符串,泛型TString,可读取所有媒体类型(*/*),可通过supportedMediaTypes属性指定媒体类型。响应信息的媒体类型为text/plain(即Content-Type的值)

(续表)

属性名称

    

ByteArrayHttpMessageConverter

读写二进制数据,泛型Tbyte[]类型,可读取*/*,可通过supportedMediaTypes属性指定媒体类型,响应信息媒体类型为application/octer-stream

MarshallingHttpMessageConverter

泛型TObject,可读取text/xmlapplication/xml媒体类型请求,响应信息的媒体类型为text/xmlapplication/xml

Jaxb2RootElementHttpMessageConverter

通过JAXB2读写XML信息,将请求消息转换到标注XmlRootElementXmlType注解的类中,泛型TObject,可读取text/xmlapplication/xml媒体类型请求,响应信息的媒体类型为text/xmlapplication/xml

MappingJacksonHttpMessageConverter

利用jacksonObjectMapper读写JSON数据,泛型TObject,可读取application/json,响应媒体类型为application/json

FormHttpMessageConverter

表单与 MultiValueMap的相互转换。读支持响应MediaTypeapplication/x-www-form-urlencoded,写支持的响应类型为application/x-www-form-urlencodedmultipart/form-data

SourceHttpMessageConverter

数据与javax.xml.transform.Source的相互转换。读支持响应MediaTypetext/xml application/xml,写支持的响应类型为text/xml application/xml

BufferedImageHttpMessageConverter

数据与java.awt.image.BufferedImage的相互转换。读支持 MediaTypeJava I/O API的所有类型,写支持的响应类型为Java I/O API的所有类型。

除了表6-9 中列出的HttpMessageConverter接口实现类外,还有很多实现类无法一一 述,读者可以自己阅读Spring MVC 源码学习。

6.3.2  RequestMappingHandlerAdapter

S pring MVC 中配置<mvc:annotation-driven/> 标签注册三个bean

RequestMappingHandlerMappingRequestMappingHandlerAdapterDefaultHandlerExceptionResolver


DispatcherServlet 默认已经装配RequestMappingHandlerAdapter 作为HandlerAdapter 组件的实现类,即HttpMessageConverter RequestMappingHandlerAdapter 使用,将请求信息转换为对象,或者将对象转换为响应信息。RequestMappingHandlerAdapter 具体源码如下所示:
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {//省略大量代码private List<HttpMessageConverter<?>> messageConverters;  public RequestMappingHandlerAdapter() { StringHttpMessageConverter stringHttpMessageConverter= new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList<>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); this.messageConverters.add(new SourceHttpMessageConverter<>()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }}


RequestMappingHandlerAdapter 源码可知,RequestMappingHandlerAdapter 构造方法默认已经装配了 下的HttpMessageConverter

l ByteArrayHttpMessageConverterl StringHttpMessageConverterl SourceHttpMessageConverterl AllEncompassingFormHttpMessageConverter



6.3.3  自定义HttpMessageConverter

如果需要装配其他类型的 HttpMessageConverter ,则可以在SpringWeb容器的上下文中自定义一个 RequestMappingHandlerAdapter 。需要注意的是,如果在SpringWeb容器的上下文中自定义一个RequestMappingHandlerAdapter,那么Spring  MVC RequestMappingHandlerAdapter 默认装配的HttpMessageConverter 将不再起作用。spring-mvc .xml 配置信息具体代码如下:

<!-- 自定义RequestMappingHandlerAdapter --><bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/> </list> </property></bean>


<list> 标签中除了使用Spring MVC 为我们提供的HttpMessageConverter 接口实现类外,还可以通过继承AbstractHttpMessageConverter 实现适合自己的HttpMessageConverter