-
Spring Mvc 中,在Java类上标注
@Controller
,即可使得该 Java 类成为一个能处理 HTTP 请求的控制器。 -
用户可以创建不限数量的控制器,分别处理不同的业务请求,如
UserController、LogController
等。每个控制器拥有多个处理请求的方法,每个方法负责不同的请求操作。 -
Spring Mvc 框架使用
@RequestMapping
将请求映射到对应的控制器方法。在框架中,在初始化Bean过程中,会把每个控制器的方法封装成一个RequestMappingInfo
对象,接收请求时,由请求映射处理器handlerMappings
对请求作匹配映射到具体的RequestMappingInfo
对象,转化成统一请求处理对象。(一般情况) -
将请求映射到控制器处理方法的工作包含一系列映射规则,这些规则是根据请求中的各种信息制定的,具体包括请求URL、请求参数、请求方法、请求头这四个方面的信息项。
-
参数获取以及模型数据返回的所有功能实现,是基于框架的参数解析器(返回值解析器)实现的。简单理解,框架目前有多少参数解析器(返回值解析器)实现类型,这具有多少中实现参数获取、模型数据返回的方式。
使用 @RequestMapping
映射请求
通过请求URL进行映射
@RequestMapping
使用value属性指定请求的URL路径,如@RequestMapping(value = "/user")
。
@RequestMapping
在类定义处指定的URL相对于应用的部署路径,而在方法定义处指定的URL则是相对于类定义处指定的URL。@RequestMapping
在类定义处未标注,则方法上标注的URL则是对于应用部署路径的。
- 同一个控制器类的多个处理方法负责处理相同业务的不同操作,最佳实践设计应该是将这些操作请求安排在某一个相同的URL之下,如:都是用户
user
模块的不同业务方法,放在相同的/user/XXX
下面。
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "/getParam")
public String getParam() {
return "user";
}
@RequestMapping(value = "/getName")
public String getName(String name) {
return "user";
}
}
@RequestMapping
支持Ant风格(?、*、**
)的 URL
/user/*/getUser:匹配 /user/aaa/getUser、/user/bbb/getUser 等 URL
/user/**/getUser:匹配 /user/getUser、/user/aaa/getUser、/user/aaa/bbb/getUser 等 URL
/user/getUser??:匹配 /user/getUserAA、/user/getUserBB 等 URL
使用 URI Template Patterns 风格
@RequestMapping
带{XXX}占位符的 URL,并且通过@PathVariable
注解可以将占位符参数{XXX}绑定到控制器方法的入参@PathVariable("XXX")
中。- 类定义处的
@RequestMapping
的URL 如果使用占位符参数,也可以绑定到处理方法的入参中
@RequestMapping("/getURITemplate/{name}")
public String getURITemplate(@PathVariable("name") String name, Model model) {
... ...
}
- 占位符还支持正则表达式:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
通过请求参数、请求方法/请求头进行映射
@RequestMapping
除了可以使用请求的URL映射请求外,还支持使用请求方法、请求头参数以及请求参数映射请求。如:
属性 | 注释 |
---|---|
value | 请求 URL |
method | 请求方法 |
params | 请求参数 |
headers | 请求头 |
consumes | 接收的 Content-Type 类型 |
produces | 返回的 Content-Type 类型 |
@RequestMapping(value = "/get", method = RequestMethod.GET,
headers = {"content-type=text/*"},
params = {"name"},
consumes = {"text/plain", "application/*"},
produces = {"text/plain", "application/*"})
public String get(@RequestParam("name") String name, ModelMap modelMap) {
User user = userService.get(name);
modelMap.addAttribute("user", user);
return "user";
}
params
、headers
、consumes
、produces
都支持简单的映射表达式,如:
params = {"name"}:请求参数必须包含 name 参数
params = {"!name"}:请求参数不能包含 name 参数
params = {"name=sam"}:请求参数必须包含 name 参数,并且其值为 sam
params = {"name!=sam"}:请求参数必须包含 name 参数,并且其值不能为 sam
@RequestMapping
请求方法组合注解
@GetMapping
、@PostMapping
是@RequestMapping
和请求方法RequestMethod.XXX
组合在一起的组合注解。
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {}
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {}
- 其他用法不变
@GetMapping(value = "/getMapping",
headers = {"content-type=text/*"},
params = {"name"},
consumes = {"text/plain", "application/*"},
produces = {"text/plain", "application/*"})
public String getMapping(String name, ModelMap modelMap) {
}
请求处理方法参数绑定(参数获取)
使用@RequestParam
绑定请求参数
- 在方法参数前面添加
@RequestParam
注解,其有三个属性:
value
:参数名称required
:是否必须defaultValue
:默认参数值
@RequestMapping(value = "/getNameAnnotation")
public String getNameAnnotation(@RequestParam(value = "name", required = false, defaultValue = "eason") String name) {
... ...
}
使用@CookieValue
绑定请求中的Cookie值
- 使用
@CookieValue
可以让处理方法入参绑定某个 Cookie 的值,它和@RequestParam
注解有三个一样属性。 - 属性
value
指定的是Cookie
的名称。
@RequestMapping(value = "/getCookieValue")
public String getCookieValue(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
System.out.println(sessionId);
... ...
}
使用 @RequestHeader
绑定请求报文头的属性值
- HTTP请求报文包含若干报文头属性,可以使用
@RequestHeade
将报文头属性绑定到处理方法的入参中 - 它和
@RequestParam
注解有三个一样属性。
@RequestMapping(value = "/getRequestHeader")
public String getRequestHeader(@RequestHeader("Accept-Encoding") String encoding) {
System.out.println(encoding);
... ...
}
使用参数/表单对象绑定请求参数值
- 直接使用参数/表单对象,Spring Mvc 会按照请求参数名和参数名、表单对象属性名匹配的方式,自动填充参数与属性。支持级联的属性名,如
user.name
、user.address.street
# 访问地址:/user/getName?name=sam
@RequestMapping(value = "/getName")
public String getName(String name) {
System.out.println(name);
... ...
}
# 访问地址:/user/getObject?name=sam&birthday=2018-09-23
@RequestMapping(value = "/getObject")
public String getObject(User user, ModelMap modelMap) {
System.out.println(user);
... ...
}
- 在使用表单对象的时候,会涉及到参数绑定格式化以及数据转换的问题,需要
InitBinder
支持,如时间类型的转换:
@InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
@InitBinder
protected void initBinder2(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
- 参数绑定,详细参考 SpringMVC4.x 笔记(5):方法的数据绑定
使用 Servlet API 对象作为入参
- 可以使用 Servlet API 的类作为处理方法的入参。如
HttpServletRequest、HttpSession
@RequestMapping(value = "/getParam")
public String getParam(HttpServletRequest request) {
String name = request.getParameter("name");
... ...
}
@RequestMapping(value = "/getParam/session")
public String getParam(HttpSession session) {
session.setAttribute("sessionId", "123456");
... ...
}
-
在使用 Servlet API 的类作为入参时,Spring Mvc 框架会自动将Web 层对应的 Servlet 对象传递给处理方法的入参,且可以同时使用Servlet API 和其他符合要求的入参。
-
【注意】在Spring Mvc 框架中,
HttpServletRequest
已经作为一个特殊性Bean存在于容器中,可以不使用方法级别的参数,而直接使用注入方式实现:
@Autowired
private HttpServletRequest httpServletRequest;
@RequestMapping(value = "/getParam")
public String getParam() {
String name = httpServletRequest.getParameter("name");
... ...
}
使用IO对象作为入参
Spring Mvc 支持控制器的处理方法使用InputStream、OutputStream、Reader、Writer
等作为方法的入参,框架将获取ServletRequest
的InputStream/Reader
或者ServletResponse
的OutputStream/Writer
,然后传递给控制器的处理方法
@RequestMapping(value = "/getIO")
public void getIO(OutputStream os) throws IOException {
// 读取类路径下的图片文件
ClassPathResource resource = new ClassPathResource("/image.jpg");
// 将图片写到输出流中
FileCopyUtils.copy(resource.getInputStream(), os);
}
使用矩阵变量绑定参数
- Spring Mvc 框架支持在URL中包含
name-value
的矩阵形式规范,其通过注解@MatrixVariable
实现。 - 在 Matrix Variable中:
- 多个变量可以分号
;
分隔。如name=sam;mobile=123456
- 一个变量多个值,可以使用逗号
,
分隔。如name=sam,name=eason
/**
* 矩阵变量:/user/getMatrixVariable/sam;mobile=123
*/
@RequestMapping("/getMatrixVariable/{name}")
public String getMatrixVariable(@PathVariable String name,
@MatrixVariable(required = false) String mobile,
Model model) {
System.out.println(mobile);
User user = userService.get(name);
model.addAttribute("user", user);
return "user";
}
- 该功能默认是关闭的(不同的版本略有不同),如需要使用则必须开启,且配合 URI Template Patterns 使用。开启的方式如下:
# JavaConfig 配置实现
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
# XMl 配置方式实现
<mvc:annotation-driven enable-matrix-variables="true"/>
其他类型的参数
- 控制器方法的参数还支持其他类型的参数:如
Local
等 - 多种不同的参数绑定实现形式,都是通过框架的参数解析器完成的,支持自定义参数解析器,从而实现私有的参数实现形式。
处理模型数据
-
对于MVC框架来说,模型数据是最重要的,因为控制(C)是为了产生模型数据(M),而视图(V)则是为了渲染模型数据
-
Spring Mvc 框架使用
@RequestMapping
将请求绑定到处理方法上,使用合适的参数绑定方式将请求消息绑定到入参中。 -
将模型数据展示给视图,Spring Mvc 提供了多种途径输出模型数据:
类型 | 注释 |
---|---|
ModelAndView | 请求处理方法返回值类型为ModelAndView 时,直接添加模型数据放入该对象 |
@ModelAttribute | 在方法入参标注该注解后,入参的对象就会放到数据模型中。 |
Model 、Map 、Model | 如果方法入参为Model 、ModelMap 、Map ,则当方法返回时,该入参对象中数据自动添加到模型中 |
@SessionAttributes | 将模型中的某个属性暂存到HttpSession 中,以便多个请求之间可以共享这个属性 |
ModelAndView
- 请求处理方法返回值类型为
ModelAndView
时,该对象包含视图信息和模型数据,框架直接使用视图对模型数据进行渲染。 - 在方法处理中,直接使用
ModelAndView
对象的方法添加模型属性。
@RequestMapping(value = "/returnModelAndView")
public ModelAndView returnModelAndView(String name) {
User user = userService.get(name);
ModelAndView modelAndView = new ModelAndView();
# 添加模型数据
modelAndView.addObject("user", user);
# 添加视图名称
modelAndView.setViewName("user");
return modelAndView;
}
Model、ModelMap、Map
- 如果方法入参为
Model
、ModelMap
、Map
,则当方法返回时,框架会在该入参对象中数据自动添加到模型中。 - 在方法处理中,只需要添加模型数据到
Model
、ModelMap
、Map
对象中即可。
@GetMapping(value = "/returnString")
public String returnString(String name, ModelMap modelMap) {
User user = userService.get(name);
modelMap.addAttribute("user", user);
return "user";
}
@RequestMapping(value = "/returnModel")
public String returnModel(String name, Model model) {
User user = userService.get(name);
model.addAttribute("user", user);
return "user";
}
@RequestMapping(value = "/returnMap")
public String returnMap(String name, Map<String, Object> map) {
User user = userService.get(name);
map.put("user", user);
return "user";
}
@ModelAttribute
- 在方法入参标注
@ModelAttribute
注解后,框架会将该参数对象放到数据模型中。
# 框架将请求信息绑定到 User 对象中
@RequestMapping(value = "/returnModelAttribute")
public String returnModelAttribute(@ModelAttribute User user) {
user.setName("Eason");
# 以user为Key将 User 对象直接会被框架放入模型数据中
user = userService.get(user.getName());
# 返回视图名
return "user";
}
- 除了在方法参数上使用
@ModelAttribute
注解,还可以在方法定义上使用该注解。Spring Mvc框架在调用目标方法前,会先逐个调用在方法级别上标注了@ModelAttribute
注解的方法,并将这些方法的返回值添加到模型中。
# 注解标注在方法上。
# 调用这个 Controller 类中的任何一个请求处理方法前,框架都会先执行该方法,并将返回值以user为键添加到模型中
@ModelAttribute("user")
public User getUser() {
return userService.get("eason");
}
# 没有任何入参数据,但是框架会把 @ModelAttribute("user") 模型数据给到这个方法
@RequestMapping(value = "/returnModelAttributeMethod")
public String returnModelAttribute(ModelMap modelMap) {
System.out.println("参数: " + modelMap.get("user"));
return "user";
}
@SessionAttributes
- 在多个请求之间共用某个模型属性数据,可以在控制器类中标注
@SessionAttributes
注解,框架会将模型中对应的属性暂存到HttpSession
中 @SessionAttributes
支持多个属性,如value
、type
等。SessionStatus
入参,可以控制会话的一些属性功能。
@Controller
@RequestMapping("user/session")
# 标注注解。 框架会将本处理器类中任何处理方法中属性名为user的模型属性透明的暂存到 HttpSession 中
@SessionAttributes("user")
public class UserSessionController {
@RequestMapping(value = "/get")
public String get(User user, Model model) {
user = userService.get(user.getName());
# 一般默认是 request 请求域的
# @SessionAttributes 则会把属性名为user的这个数据放入 session 请求域
model.addAttribute("user", user);
return "user";
}
# 在多个请求之间共用某个模型属性数据,隐含模型自动从HttpSession 中获取到这个模型属性
@RequestMapping(value = "/return")
public String get(ModelMap modelMap, SessionStatus sessionStatus) {
# 读取模型中的数据
User user = (User) modelMap.get("user");
if (Objects.nonNull(user)) {
user.setName("Rabbi");
# 清除本处理器对应的会话属性
sessionStatus.setComplete();
}
return "user";
}
}
转发与重定向模型数据传输
- 一般情况下,请求控制方法返回字符串类型的值会被解析成逻辑视图名称,可以使用
forward
、redirect
前缀进行转发与重定向的特殊处理。 forward
请求是与当前请求同属于一个请求;redirect
请求会让浏览器重新发起一个新的请求forward
请求实现:forward: + / + ClassUrl + methodUrl
,属于同一次请求,数据模型会传递
@Controller
@RequestMapping("user/forwardAndRedirect")
public class UserForwardAndRedirectController {
# forward: 同一次请求,数据模型会传递
@RequestMapping(value = "/get")
public String get(User user) {
System.out.println("参数: " + user);
return "user";
}
@RequestMapping(value = "/forward")
public String forward(User user, ModelMap modelMap) {
# 隐藏的数据模型,会把 user 对象放入数据模型,这时直接就可以从 ModelMap 中获取
System.out.println("参数: " + modelMap.get("user"));
return "forward:/user/forwardAndRedirect/get";
}
}
redirect
请求实现:
- 同一个控制器类中重定向:
redirect + MethodUrl
,不要/
开头 - 不同的控制器类重定向:
redirect + / + ClassUrl + MethodUrl
,必须/
开头 - 重定向请求,是两个请求,需要使用
RedirectAttributes#addFlashAttribute
处理模型数据的传递
@Controller
@RequestMapping("user/forwardAndRedirect")
public class UserForwardAndRedirectController {
@RequestMapping(value = "/getName")
public String getName(User user) {
System.out.println("参数: " + user);
return "user";
}
@RequestMapping(value = "/redirect")
public String redirect(User user, RedirectAttributes model) {
System.out.println("参数: " + user);
// 重定向,两个请求,需要使用 RedirectAttributes#addFlashAttribute 方法
model.addFlashAttribute("user", user);
return "redirect:getName";
}
@RequestMapping(value = "/redirect2")
public String redirect2(User user, RedirectAttributes model) {
System.out.println("参数: " + user);
model.addFlashAttribute("user", user);
return "redirect:/user/forwardAndRedirect/getName";
}
}
使用 HttpMessageConverter 获取参数与返回数据模型
-
HttpMessageConverter
是Spring 的一个重要接口,负责将请求信息转换为一个对象,将对象输出为响应信息 -
HttpMessageConverter
接口有大量的实现类:如
StringHttpMessageConverter
:支持将请求信息转换为字符串MappingJackson2HttpMessageConverter
:利用Jackson
读写JSON数据
使用 HttpMessageConverter
- 使用
HttpMessageConverter
将请求信息装换并绑定到处理方法的参数,Spring Mvc 提供了两种方式:
方式 | 注释 |
---|---|
注解 @ResponseBody 、@RequestBody | 1. 对处理方法进行标注。 2. @RestController 注解组合了 @Controller 、@ResponseBody 两个注解 |
RequestEntity 、ResponseEntity 类 | 作为处理方法的入参或者返回值 |
- 使用注解
@ResponseBody
、@RequestBody
的例子
# 将请求报文体转换为字符串绑定到 @RequestBody 入参中
# 匹配 StringHttpMessageConverter
@RequestMapping(value = "/RequestBody")
public String RequestBody(@RequestBody String name) {
System.out.println(name);
return "user";
}
# 将数据传输到响应流中
# 匹配 ByteArrayHttpMessageConverter
@ResponseBody
@RequestMapping(value = "/responseBody/{name}")
public byte[] responseBodyByte(@PathVariable("name") String name) {
System.out.println(name);
return name.getBytes();
}
- 使用类
RequestEntity
、ResponseEntity
的例子
@RequestMapping(value = "/requestEntity")
public String RequestBody(RequestEntity<String> entity) {
System.out.println(entity.getBody());
return "user";
}
@RequestMapping(value = "/responseEntity")
public ResponseEntity<byte[]> responseEntity() {
return new ResponseEntity("Sam".getBytes(), HttpStatus.OK);
}
处理 XML/JSON 类型数据
- Spring Mvc 提供了可以处理XML和JSON格式的请求/响应消息的
HttpMessageConverter
。
类型 | HttpMessageConverter类型 |
---|---|
XML | MarshallingHttpMessageConverter 、Jaxb2RootElementHttpMessageConverter |
JSON | GsonHttpMessageConverter 、MappingJackson2HttpMessageConverter 、FastJsonHttpMessageConverter4 |
- 【注意】
@ResponseBody
、@RequestBody
注解默认只添加了一部分HttpMessageConverter
不足以支持XML/JSON的处理,需要@EnableWebMvc
注解或者<mvc:annotation-driven/>
标签的支持