一、SpringMVC参数绑定
我们可以回忆一下,在struts2中,是通过在Action中定义一个成员变量来接收前台传进来的参数。
在 SpringMVC 中,提交请求的数据是通过方法形参来接收的。从客户端请求的 key/value 数据,经过参数绑定,将 key/value 数据绑定到 Controller 的形参上,然后在 Controller 就可以直接使用该形参。
这里涉及到参数绑定组件,那么什么是参数组件,这里可以先理解为将请求的数据转换为我们需要的数据称为参数绑定组件,也就是参数绑定转换器。SpringMVC 内置了很多参数转换器,只有在极少数情况下需要我们自定义参数转换器。
二、默认支持的类型
SpringMVC 有支持的默认参数类型,我们直接在形参上给出这些默认类型的声明,就能直接使用了
① HttpServletRequest对象 ② HttpServletResponse对象 ③ HttpSession对象 ④ Model/ModelMap对象
在参数绑定过程中,如果遇到上面类型就直接进行绑定。也就是说,我们可以在controller的方法的形参中直接定义上面这些类型的参数,springmvc会自动绑定。
Model/ModelMap,ModelMap是Model接口的一个实现类,作用是将Model数据填充到request域,即使使用Model接口,其内部绑定还是由ModelMap来实现。
三、SpringMVC的各种参数绑定方式
1)简单类型绑定
1、基本数据类型(以int为例,其它类似)
① byte:占用一个字节,取值范围为 -128-127,默认是“u0000”,表示空 ② short:占用两个字节,取值范围为 -32768-32767 ③ int:占用四个字节,-2147483648-2147483647 ④ long:占用八个字节,对 long型变量赋值时必须加上"L"或“l”,否则不认为是 long型 ⑤ float:占用四个字节,对 float型进行赋值的时候必须加上“F”或“f”,如果不加,会产生编译错误,因为系统自动将其定义为 double型变量。double转换为float类型数据会损失精度。floata = 12.23产生编译错误的,floata = 12是正确的 ⑥ double:占用八个字节,对 double型变量赋值的时候最好加上“D”或“d”,但加不加不是硬性规定 ⑦ char:占用两个字节,在定义字符型变量时,要用单引号括起来 ⑧ boolean:只有两个值“true”和“false”,默认值为false,不能用0或非0来代替,这点和C语言不同
前端代码:
<a herf="${pageContext.request.contextPath }/delete.do?id=${item_id }">删除</a>
Controller代码:
@RequestMapping(value = "delete.do") public String delete(int id) { itemService.deleteById(id); return "redirect:allList.do"; }
ajax提交数据的id的key和Controller的参数变量名保持一致,就能完成数据绑定,如果不一致可以使用@RequestParam注解。
public String delete(@RequestParam(value = "deleteId") int id)
需要注意的是,如果Controller方法参数中定义的是基本数据类型,但是从页面提交过来的数据为null或者”"的话,会出现数据转换的异常。
也就是必须保证表单传递过来的数据不能为null或”",所以,在开发过程中,对可能为空的数据,最好将参数数据类型定义成包装类型,具体参见下面的例子。
2、包装类型
包装类型:Integer、Long、Byte、Double、Float、Short,(String 类型在这也是适用的)
前端表单代码:
<form action="${pageContext.request.contextPath }/getItemByName.do" method="post"> <input type="text" name="item_name" placeholder="name" /> <input type="submit" value="查询"> </form>
Controller代码:
@RequestMapping(value = "getItemByName.do",method = RequestMethod.POST ) public String select(String name) { // 查询 ItemInfo item = itemService.selectItemInfoByName(name); // 创建一个集合,存储查询到的item List<ItemInfo> list = new ArrayList<ItemInfo>(); list.add(item); // model保存数据 model.addAttribute("itemList", list); // 返回视图名 return "item_list"; }
和基本数据类型基本一样,不同之处在于,表单传递过来的数据可以为null或”",以上面代码为例,如果表单中num为”"或者表单中无num这个input,那么,Controller方法参数中的num值则为null。
3、POJO (实体类) 类型
package com.sikiedu.bean; public class ItemInfo { private String item_id; private String item_name; private String item_type; private Double item_price; public String getItem_id() { return item_id; } public void setItem_id(String item_id) { this.item_id = item_id; } public String getItem_name() { return item_name; } public void setItem_name(String item_name) { this.item_name = item_name; } public String getItem_type() { return item_type; } public void setItem_type(String item_type) { this.item_type = item_type; } public Double getItem_price() { return item_price; } public void setItem_price(Double item_price) { this.item_price = item_price; } @Override public String toString() { return "ItemInfo [item_id=" + item_id + ", item_name=" + item_name + ", item_type=" + item_type + ", item_price=" + item_price + "]"; }
表单代码:name 属性值和 POJO 实体类的属性保持一致即可映射成功。
<form action="${pageContext.request.contextPath }/save.do" method="post"> ID:<input type="hidden" name="item_id" /> 类型:<input type="text" name="item_name" /> 名称:<input type="text" name="item_type" /> 原价:<input type="text" name="item_price" /> </form>
Controller代码:
@RequestMapping(value = "save.do",method = RequestMethod.POST) public String save(ItemInfo item, Model model) { // 保存前端传递过来的item itemService.save(item); model.addAttribute("success", true); // 重定向到列表页 return "redirect:allList.do"; }
非常简单,只需将对象的属性名和input的name值一一匹配即可。
4、复合POJO (实体类) 类型
在ItemInfoVo类中添加一个属性ItemInfo
package com.sikiedu.bean; import java.util.List; public class ItemInfoVo { private ItemInfo itemInfo; private String[] ids; private List<Double> priceList; public List<Double> getPriceList() { return priceList; } public void setPriceList(List<Double> priceList) { this.priceList = priceList; } public String[] getIds() { return ids; } public void setIds(String[] ids) { this.ids = ids; } public ItemInfo getItemInfo() { return itemInfo; } public void setItemInfo(ItemInfo itemInfo) { this.itemInfo = itemInfo; } }
表单代码:
<form action="${pageContext.request.contextPath }/getItemByVo.do" method="post"> <input type="text" name="itemInfo.item_name"/> <input type="text" name="itemInfo.item_type"/> <input type="text" name="itemInfo.item_price"/> <input type="submit" value="查询"> </form>
Controller代码:
@RequestMapping(value = "getItemByVo.do")
public String selectByVo(ItemInfoVo vo, Model model) throws MyException { // 查询 List<ItemInfo> itemList = itemService.selectByVo(vo); // 保存数据 model.addAttribute("itemList", itemList); // 返回视图名称 return "item_list"; }
ItemInfoVo对象中有itemInfo属性,但是,在表单代码中,需要使用“属性名(对象类型的属性).属性名”来命名input的name。
2)复杂类型绑定
1、数组类型绑定
请求参数:key和形参名称或者属性名称要一致
selectArrays.do?ids=111&ids=222&ids=333&ids=444&ids=555&ids=666
Controller代码:形参的定义的name与前端页面保持一致
@RequestMapping(value = "selectArrays.do") public String selectArrays(String[] ids) { System.out.println(ids.length); for (String id : ids) { System.out.println(id); } return "redirect:allList.do"; }
2、List集合绑定
List需要绑定在对象上,而不能直接写在Controller方法的参数中
package com.sikiedu.bean; import java.util.List; public class ItemInfoVo { private List<Double> priceList; public List<Double> getPriceList() { return priceList; } public void setPriceList(List<Double> priceList) { this.priceList = priceList; } }
前端代码:
<form action="${pageContext.request.contextPath }/selectVoList.do" method="post"> <input type="hidden" name="priceList[0]" value="${item.item_price }" /> <input type="hidden" name="priceList[1]" value="${item.item_price }" /> <input type="hidden" name="priceList[2]" value="${item.item_price }" /> <input type="hidden" name="priceList[3]" value="${item.item_price }" /> <input type="hidden" name="priceList[4]" value="${item.item_price }" /> <input type="submit" value="获取数组" /> </form>
Controller代码:
@RequestMapping(value = "selectVoList.do") public String selectVoList(ItemInfoVo vo) {
System.out.println("ListSize : "+vo.getPriceList().size()); for (Double price : vo.getPriceList()) { System.out.println("price = " + price); } return "redirect:allList.do"; }
通过类似于list[i].name这种形式来传递参数的。list[i]表示第i个priceList,然后 list[i].属性 表示第i个priceList中对应的属性。
1+2、使用@RequestBody直接绑定 数组 / List
前端代码:提交Json数据
<form action="#" method="post"> <input class="nameList" type="hidden" value="啊啊啊啊" /> <input class="nameList" type="hidden" value="是是是是" /> <input class="nameList" type="hidden" value="呀呀呀呀" /> <input class="nameList" type="hidden" value="嚯嚯嚯嚯" /> <input class="nameList" type="hidden" value="鹅鹅鹅鹅" /> <input class="nameList" type="button" value="获取所有name" onclick="getName()" /> </form>
function getName() { // 创建一个数组 var nameList = new Array(); // 使用each循环 遍历该class所有name的值 并添加到数组中 $(".nameList").each(function() { // push添加到数组中 nameList.push($(this).val()); }); $.ajax({ type : "post", /* url : "${pageContext.request.contextPath }/getNameArray.do", */ url : "${pageContext.request.contextPath }/getNameList.do", contentType : "application/json;charset=UTF-8", data : JSON.stringify(nameList) }); }
Controller代码:
@RequestMapping(value = "getNameList.do") public void getNameList(@RequestBody List<String> nameList) { // code... } @RequestMapping(value = "getNameArray.do") public void getNameArray(@RequestBody String[] array) { // code... }
3、Set集合绑定
Set和基本和List绑定类似,也需要绑定在对象上,而不能直接写在Controller方法的参数中。
但是,绑定Set数据时,必须先在Set对象中add相应的数量的模型对象。
POJO类:
public class User { private String firstName; private String lastName; // get* And Set* } public class UserSetForm { private Set<User> users = new HashSet<User>(); public UserSetForm() { users.add(new User()); users.add(new User()); users.add(new User()); } // get* And Set* }
表单代码:
<form action="getSetForm.do" method="post"> <input name="users[0].firstName" value="aaa" /> <input name="users[0].lastName" value="bbb" /> <input name="users[1].firstName" value="ccc" /> <input name="users[1].lastName" value="ddd" /> <input name="users[2].firstName" value="eee" /> <input name="users[2].lastName" value="fff" /> <input type="submit" value="Save" /> </form>
Controller代码:
@RequestMapping("getSetForm.do") public void test(UserSetForm userForm) { for (User user : userForm.getUsers()) { System.out.println(user.getFirstName() + " - " + user.getLastName()); } }
需要特别提醒的是,如果最大下标值大于Set的size,则会抛出org.springframework.beans.InvalidPropertyException异常。
所以,在使用时有些不便。
4、Map集合绑定
Map最为灵活,它也需要绑定在对象上,而不能直接写在Controller方法的参数中。
① 需要使用POJO类型来接受,POJO类型中有一个Map类型的参数。
② Map类型的属性和名称,要求和请求参数[]前面的名称一致。
③ Map的key要唯一,value要求是一个POJO类型。
POJO类:
public class User { private String firstName; private String lastName; // get* And set* } public class UserMapForm { private Map<String, User> users; // get* And set* }
表单代码:
<form action="getMapForm.do" method="post"> <input type="submit" value="Save" /> <input name="users['a'].firstName" value="aaa" /> <input name="users['a'].lastName" value="bbb" /> <input name="users['b'].firstName" value="ccc" /> <input name="users['b'].lastName" value="ddd" /> <input name="users['c'].firstName" value="eee" /> <input name="users['c'].lastName" value="fff" /> </form>
Controller代码:
@RequestMapping("getMapForm.do") public void test(UserMapForm userForm) { for (Map.Entry<String, User> entry : userForm.getUsers().entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue().getFirstName() + " - " + entry.getValue().getLastName()); } }
3)解决表单post提交乱码:在web.xml中配置过滤器;
<!-- 过滤器 - 解决中文乱码 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>