1、参数绑定综述
我们知道,SpringMVC是用来处理前端的一些请求,当用户在页面触发某种请求时,一般会将一些参数(key/value)带到后台,然后将数据再通过视图返回给用户。在Spring MVC中可以通过参数绑定,将客户端请求的key/value数据绑定到Controller处理器方法的形参上,显然,这是很关键的一个问题。
当用户发送一个请求时,根据Spring MVC的请求处理流程,前端控制器会请求处理器映射器HandlerMapping返回一个处理器(或处理器链),然后请求处理器适配器HandlerAdapter执行相应的Handler处理器。此时,处理器适配器HandlerAdapter会调用Spring MVC提供的参数绑定组件将请求的key/value数据绑定到Controller处理器方法对应的形参上。
Spring MVC中有一些默认支持的请求类型,这些类型可以直接在Controller类的方法中定义,在参数绑定的过程中遇到该种类型就直接进行绑定。其默认支持的类型有以下几种:HttpServletRequest、HttpServletResponse、HttpSession及Model/ModelMap。
- HttpServletRequest可以通过request对象获取请求信息。
- HttpServletResponse可以通过response对象处理响应信息。
- HttpSession可以通过session对象得到session中存放的对象。
- Model是一个接口,ModelMap是一个接口实现,它的作用就是将Model数据填充到request域,跟ModelAndView类似(ModelAndView表示封装了Model和View的对象,推荐使用它)。
在参数绑定过程中,如果遇到上面类型就直接进行绑定。也就是说,我们可以在Controller的方法的形参中直接定义上面这些类型的参数,然后SpringMVC会自动绑定。
注意:从前端发送过来的请求参数都是String类型的,所以SpringMVC还提供了一些默认的格式转换器,这些格式转换器会自动根据数据类型进行类型转换。比如age会自动由字符串转换成了int类型。
2、简单类型参数绑定
对于简单类型的参数,这些类型是直接在Controller类的方法中定义,当用户发送请求后,Spring MVC在处理key/value信息时,就会以key名寻找Controller类的方法中具有相同名称的形参并进行绑定,例如下面的例子,从前台页面提交图书的信息,然后在后台打印传来的数据。具体Controller代码如下:
注意:Controller中的形参需要与前台提交的name属性一样才能完成绑定,如果请求中不包含其中的某个形参,此时是不会报错的,默认使用该参数时要进行空校验。
[1]、创建用于处理前台请求的Controller处理器:
//@Controller:表示一个Controller实例,该实例由Spring容器管理
@Controller
public class BookController {
//配置请求的地址
@RequestMapping(value = "book", method = RequestMethod.POST)
public String getInfo(Integer id, String name, String publisher) {
System.out.println("获取到的数据为:ID--" + id + ",名称--" + name + ",出版社--" + publisher);
//成功后跳转的页面
return "success";
}
}
[2]、编写用于提交图书信息的页面(index.jsp):
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>首页</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/book" method="post">
<table>
<tr>
<td>图书ID:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>图书名称:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>出版社:</td>
<td><input type="text" name="publisher"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
[3]、然后启动Tomcat进行测试:
3、单一POJO类型绑定
上面简单参数类型绑定都是需要在Controller中编写一个个的形参才能完成绑定,但是我们在实际开发中一般不这么干,因为有可能前台提交的数据项很多而且复杂,如果这样写就要在Controller中编写大量形参来接收,极大的降低了开发效率,平时一般都会使用POJO类型来绑定数据。
[1]、创建一个Book实体类:
public class Book {
//书的ID
private Integer id;
//书名
private String name;
//出版社
private String publisher;
//getter,setter和toString省略
}
[2]、将上面Controller代码中接收的参数修改为一个pojo类型,如下所示:
//@Controller:表示一个Controller实例,该实例由Spring容器管理
@Controller
public class BookController {
//配置请求的地址
@RequestMapping(value = "book", method = RequestMethod.POST)
public String getInfo(Book book) {
System.out.println("获取到的数据为:ID--" + book.getId()
+ ",名称--" + book.getName()
+ ",出版社--" + book.getPublisher());
//成功后跳转的页面
return "success";
}
}
实现的效果和上面简单参数类型一模一样,但是一定要注意:前端提交的name属性必须与POJO里面的属性一致,这样SpringMVC才会自动帮我们绑定数据,否则会绑定失败!!!
4、嵌套POJO类型绑定
嵌套POJO类型就是在一个Java实体类中包含了其它的实体类,此时Spring MVC依然可以解析并成功绑定该类型的包装类。
[1]、首先创建一个用于嵌套的Author类,表示图书的作者信息,新建的Author类如下:
/**
* Author实体
*/
public class Author {
private String name;//作者名称
private String age;//作者年龄
//getter,setter和toString省略
}
[2]、创建Book类,里面添加了一个嵌套的pojo类型属性author:
public class Book {
//书的ID
private Integer id;
//书名
private String name;
//出版社
private String publisher;
//图书作者
private Author author;
//getter,setter和toString省略
}
[3]、Controller类:当前端页面发出请求后,处理器适配器会解析这种格式的name,将该参数当做POJO类的成员参数绑定起来,作为Controller方法的形参。这样在Controller方法中就可以通过POJO类获取其POJO类的其他类的对象。
//@Controller:表示一个Controller实例,该实例由Spring容器管理
@Controller
public class BookController {
//配置请求的地址
@RequestMapping(value = "book", method = RequestMethod.POST)
public String getInfo(Book book) {
System.out.println("获取到的数据为:ID--" + book.getId()
+ ",名称--" + book.getName()
+ ",出版社--" + book.getPublisher()
+ ",作者信息--" + book.getAuthor());
//成功后跳转的页面
return "success";
}
}
[4]、提交图书信息的页面(index.jsp)。注意:由于Book类包含了Author类,将其作为其属性,那么在进行提交的时候,就必须在input的name属性指定为嵌套对象.属性
的形式。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>首页</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/book" method="post">
<table>
<tr>
<td>图书ID:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>图书名称:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>出版社:</td>
<td><input type="text" name="publisher"></td>
</tr>
<tr>
<td>作者名称:</td>
<td><input type="text" name="author.name"></td>
</tr>
<tr>
<td>作者年龄:</td>
<td><input type="text" name="author.age"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
[5]、再次将项目部署到Tomcat上后,测试的结果如下所示:
5、补充:@RequestMapping
@RequestMapping注解是用来映射请求,也就是通过它来指定控制器可以处理哪些URL请求。这个注解的使用非常简单,可以在类和方法上使用。
- 用于方法上,表示映射当前Handler中的方法。
- 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
[1]、只用在方法上
@Controller
public class HelloController {
//当请求地址为 /show 的时候,这个方法会被执行
@RequestMapping(value = "/show")
public String show1(){
return "success";
}
//请求为 /show1 或者 /show2 都可以访问到该方法
@RequestMapping(value = {"/show1","/show2"})
public String show2(){
return "success";
}
}
[2]、用在类上
在我们实际的项目中,可能有很多的接口,例如用户、订单、商品等等,为了更好的区分它们,一般用户的请求都是/user/xxx格式的,订单相关的请求都是 /order/xxx 格式的,所以为了方便处理,这里的前缀(就是 /order、/user)可以统一在 Controller 上面处理。
@Controller
@RequestMapping(value = "/user")
public class HelloController {
@RequestMapping(value = "/show")
public String show1(){
return "success";
}
}
当类上加了 @RequestMapping 注解之后,此时,要想访问到 /show,地址就应该用 /user/show 来访问了。
[3]、请求方法限定
@RequestMapping 中的 method 属性主要用来定义接收浏览器发来的何种请求。在Spring中,使用枚举类RequestMethod来定义浏览器请求的方式。
@Controller
@RequestMapping(value = "/user")
public class HelloController {
//表示该方法只能被 GET 请求访问
@RequestMapping(value = "/show1",method = RequestMethod.GET)
public String show1(){
return "success";
}
//表示该方法只能被 POST 请求访问
@RequestMapping(value = "/show2",method = RequestMethod.POST)
public String show2(){
return "success";
}
//表示该方法将同时接收通过 GET 和 POST 方式发来的请求
@RequestMapping(value = "/show3",method = {RequestMethod.GET,RequestMethod.POST})
public String show3(){
return "success";
}
}
注:如果你强行用POST请求来访问GET请求的话,或报405的错误!
[4]、带占位符的URL
这个功能是Spring 3.0 新增的功能,占位符使用{}括起来,可以通过 @PathVariable 将 URL 中的占位符绑定到控制器的处理方法的参数中。带占位符的URL示例:
@Controller
@RequestMapping(path = "/user")
public class UserController {
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public String show(@PathVariable("id") Integer id) {
return "success";
}
}
在这个控制器中 show() 方法将可以接收 user/1、user/2、user/3等等的路径请求,请求的方法必须为GET,使用 @PathVariable 为应用实现 REST 规范提供了具大的便利条件。
6、补充:@RequestParam
@RequestParam用于将请求参数区域的数据映射到Handler方法的参数上。使用场景:例如在上面在绑定数据时,绑定的规则是:表单中name属性的值要和Handler方法中形参的值相对应,这样才能将数据绑定成功,否则服务端接收不到前端传来的数据。有时有一些特殊情况,前端的name属性值与后端Handler方法中的形参不一致,这个时候就可以通过 @RequestParam 注解来解决。
语法:@RequestParam(value=”参数名”,required=”true|false”,defaultValue=””)
- value:请求中传入参数的名称。
- required:该参数是否为必传项,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
- defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值。
@RequestParam注解的简单举例:
<!-- 表单数据,这里的name属性值为name。 -->
<input type="text" name="name">
/**
* -@RequestParam的使用
*/
@Controller
@RequestMapping("hello")
public class HelloController {
//表示将请求路径中参数名称为name映射为userName,相当于重命名一样
@RequestMapping("one")
public String show1(@RequestParam("name") String userName) {
System.out.println(userName);
return "success";
}
//表示请求路径中不一定要包含名称为name的参数
@RequestMapping("two")
public String show2(@RequestParam(value = "name", required = false) String userName) {
System.out.println(userName);
return "success";
}
//表示请求路径中必须包含名称为name的参数,如果name参数的值为空,则使用默认的值"hello"
@RequestMapping("three")
public String show3(@RequestParam(value = "name" ,required=true, defaultValue = "hello") String userName) {
System.out.println(userName);
return "success";
}
}