springmvc是用来处理页面的一些请求,然后将数据再通过视图返回给用户的。参数绑定,简单来说就是客户端发送请求,而请求中包含一些数据,那么这些数据怎么到达 Controller ?这在实际项目开发中也是用到的最多的,那么 SpringMVC 的参数绑定是怎么实现的呢?下面我们来详细的讲解。
1、SpringMVC 参数绑定
在 SpringMVC 中,提交请求的数据是通过方法形参来接收的。从客户端请求的 key/value 数据,经过参数绑定,将 key/value 数据绑定到 Controller 的形参上,然后在 Controller 就可以直接使用该形参。
这里涉及到参数绑定组件,那么什么是参数组件,这里可以先理解为将请求的数据转换为我们需要的数据称为参数绑定组件,也就是参数绑定转换器。SpringMVC 内置了很多参数转换器,只有在极少数情况下需要我们自定义参数转换器。
2、默认支持的类型
SpringMVC 有支持的默认参数类型,我们直接在形参上给出这些默认类型的声明,就能直接使用了。如下:
①、HttpServletRequest 对象
②、HttpServletResponse 对象
③、HttpSession 对象
④、Model/ModelMap 对象
Controller 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @RequestMapping ( "/defaultParameter" ) public ModelAndView defaultParameter(HttpServletRequest request,HttpServletResponse response, HttpSession session,Model model,ModelMap modelMap) throws Exception{ request.setAttribute( "requestParameter" , "request类型" ); response.getWriter().write( "response" ); session.setAttribute( "sessionParameter" , "session类型" ); //ModelMap是Model接口的一个实现类,作用是将Model数据填充到request域 //即使使用Model接口,其内部绑定还是由ModelMap来实现 model.addAttribute( "modelParameter" , "model类型" ); modelMap.addAttribute( "modelMapParameter" , "modelMap类型" ); ModelAndView mv = new ModelAndView(); mv.setViewName( "view/success.jsp" ); return mv; } |
表单代码:(截取主要代码)
1 2 3 4 5 6 | <body> request:${requestParameter} session:${sessionParameter} model:${modelParameter} modelMap:${modelMapParameter} </body> |
然后访问,页面显示如下:
这里我们重点说一下 Model/ModelMap,ModelMap是Model接口的一个实现类,作用是将Model数据填充到request域,即使使用Model接口,其内部绑定还是由ModelMap来实现
3、基本数据类型的绑定
哪些是基本数据类型,我们这里重新总结一下:
1 2 3 4 5 6 7 8 9 | 一、 byte ,占用一个字节,取值范围为 - 128 - 127 ,默认是“\u0000”,表示空 二、 short ,占用两个字节,取值范围为 - 32768 - 32767 三、 int ,占用四个字节,- 2147483648 - 2147483647 四、 long ,占用八个字节,对 long 型变量赋值时必须加上 "L" 或“l”,否则不认为是 long 型 五、 float ,占用四个字节,对 float 型进行赋值的时候必须加上“F”或“f”,如果不加,会产生编译错误,因为系统 自动将其定义为 double 型变量。 double 转换为 float 类型数据会损失精度。 float a = 12.23 产生编译错误的, float a = 12 是正确的 六、 double ,占用八个字节,对 double 型变量赋值的时候最好加上“D”或“d”,但加不加不是硬性规定 七、 char ,占用两个字节,在定义字符型变量时,要用单引号括起来 八、 boolean ,只有两个值“ true ”和“ false ”,默认值为 false ,不能用 0 或非 0 来代替,这点和C语言不同 |
我们以 int 类型为例:
JSP 页面代码:
1 2 3 4 | <form action= "basicData" method= "post" > <input name= "username" value= "10" type= "text" /> <input type= "submit" value= "提交" > </form> |
Controller 代码:
1 2 3 4 | @RequestMapping ( "/basicData" ) public void basicData( int username){ System.out.println(username); //10 } |
结果是 打印出了表单里面的 value 的值。
注意:表单中input的name值和Controller的参数变量名保持一致,就能完成数据绑定。那么如果不一致呢?我们使用 @RequestParam 注解来完成,如下:
JSP页面代码不变,<input name="username">保持原样,Controller 代码如下
使用注解 @RequestParam ,我们可以使用任意形参,但是注解里面的 value 属性值要和表单的name属性值一样。
问题:我们这里的参数是基本数据类型,如果从前台页面传递的值为 null 或者 “”的话,那么会出现数据转换的异常,就是必须保证表单传递过来的数据不能为null或”",所以,在开发过程中,对可能为空的数据,最好将参数数据类型定义成包装类型,具体参见下面的例子。
4、包装数据类型的绑定
包装类型如Integer、Long、Byte、Double、Float、Short,(String 类型在这也是适用的)这里我们以 Integer 为例
Controller 代码为:
和基本数据类型基本一样,不同之处在于,表单传递过来的数据可以为null或”",以上面代码为例,如果表单中num为”"或者表单中无num这个input,那么,Controller方法参数中的num值则为null。
5、POJO(实体类)类型的绑定
User.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package com.ys.po; import java.util.Date; public class User { private Integer id; private String username; private String sex; private Date birthday; public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } public String getUsername() { return username; } public void setUsername(String username) { this .username = username == null ? null : username.trim(); } public String getSex() { return sex; } public void setSex(String sex) { this .sex = sex == null ? null : sex.trim(); } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this .birthday = birthday; } } |
JSP页面:注意输入框的 name 属性值和上面 POJO 实体类的属性保持一致即可映射成功。
1 2 3 4 5 6 7 | <form action= "pojo" method= "post" > 用户id:<input type= "text" name= "id" value= "2" ></br> 用户名:<input type= "text" name= "username" value= "Marry" ></br> 性别:<input type= "text" name= "sex" value= "女" ></br> 出生日期:<input type= "text" name= "birthday" value= "2017-08-25" ></br> <input type= "submit" value= "提交" > </form> |
注意看:这里面我们数据都写死了,直接提交。有Integer类型的,String类型的,Date类型的。
Controller :
1 2 3 4 | @RequestMapping ( "/pojo" ) public void pojo(User user){ System.out.println(user); } |
我们在上面代码打个断点,然后输入URL,进入到这个Controller中:
上面是报错了,User.java 的birthday 属性是 Date 类型的,而我们输入的是字符串类型,故绑定不了
那么问题来了,Date 类型的数据绑定失败,如何解决这样的问题呢?这就是我们前面所说的需要自定义Date类型的转换器。
①、定义由String类型到 Date 类型的转换器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.ys.util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.core.convert.converter.Converter; //需要实现Converter接口,这里是将String类型转换成Date类型 public class DateConverter implements Converter<String, Date> { @Override public Date convert(String source) { //实现将字符串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); try { return dateFormat.parse(source); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果参数绑定失败返回null return null ; } } |
②、在 springmvc.xml 文件中配置转换器
1 2 3 4 5 6 7 8 | <mvc:annotation-driven conversion-service= "conversionService" ></mvc:annotation-driven> <bean id= "conversionService" class = "org.springframework.format.support.FormattingConversionServiceFactoryBean" > <property name= "converters" > <!-- 自定义转换器的类名 --> <bean class = "com.ys.util.DateConverter" ></bean> </property> </bean> |
输入 URL,再次查看Controller的形参:
6、复合POJO(实体类)类型的绑定
这里我们增加一个实体类,ContactInfo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package com.ys.po; public class ContactInfo { private Integer id; private String tel; private String address; public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } public String getTel() { return tel; } public void setTel(String tel) { this .tel = tel == null ? null : tel.trim(); } public String getAddress() { return address; } public void setAddress(String address) { this .address = address == null ? null : address.trim(); } } |
然后在上面的User.java中增加一个属性 private ContactInfo contactInfo
JSP 页面:注意属性name的命名,User.java 的复合属性名.字段名
Controller
User对象中有ContactInfo属性,但是,在表单代码中,需要使用“属性名(对象类型的属性).属性名”来命名input的name。
7、数组类型的绑定
需求:我们查询出所有User 的信息,并且在JSP页面遍历显示,这时候点击提交按钮,需要在 Controller 中获得页面中显示 User 类的 id 的所有值的数组集合。
JSP 页面:注意用户id的name值定义为 userId
Controller.java
8、List类型的绑定
需求:批量修改 User 用户的信息
第一步:创建 UserVo.java,封装 List<User> 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package com.ys.po; import java.util.List; public class UserVo { private List<User> userList; public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this .userList = userList; } } |
第二步:为了简化过程,我们直接从 Controller 中查询所有 User 信息,然后在页面显示
Controller
1 2 3 4 5 6 7 8 | @RequestMapping ( "selectAllUserAndList" ) public ModelAndView selectAllUserAndList(){ List<User> listUser = userService.selectAllUser(); ModelAndView mv = new ModelAndView(); mv.addObject( "listUser" , listUser); mv.setViewName( "list.jsp" ); return mv; } |
JSP 页面
第三步:修改页面的值后,点击提交
由于我们在 JSP 页面 input 输入框定义的name属性名是 userList[${status.index}].id 这种形式的,这里我们直接用 UserVo 就能获取页面批量提交的 User信息
9、Map类型的绑定
首先在 UserVo 里面增加一个属性 Map<String,User> userMap
第二步:JSP页面,注意看 <input >输入框 name 的属性值
第三步:Controller 中获取页面的属性
10、遇到的问题
①、form表单无法提交input输入框属性设置为 disabled 的内容
比如:
1 | <input type= "text" disabled= "disabled" name= "metadataName" maxlength= "50" placeholder= "这里输入模型英文名称" title= "模型英文名称" "/> |
具有 disabled="disabled" 的属性,提交到 Controller后,metadataName 的值为null
解决办法:改为 readonly="readonly"
readonly:针对input(text / password)和textarea有效,在设置为true的情况下,用户可以获得焦点,但是不能编辑,在提交表单时,输入项会作为form的内容提交。
disabled:针对所有表单元素(select,button,input,textarea等),在设置为disabled为true的情况下,表单输入项不能获得焦点,用户的所有操作无意义,在提交表单时,表单输入项不会被提交。