springmvc中的数据绑定(前后台传递)
一:前后端数据的传递过程中肯定会涉及到数据的绑定,因为一个个参数单独接收很费事且代码效率会大大降低,下面就对各种类型的数据进行绑定。
重点:表单大量数据(多对象)的传递绑定
1、基本类型数据绑定
在controller中写一个int参数绑定的方法,
@RequestMapping("/int") @ResponseBody public String getInt(int id){ return ""+id; }
发送请求:http://localhost:8080/int?3
响应:此时会报500错误,接收不到名为id的参数
发送请求:http://localhost:8080/int?id=3
响应:3
结论:用基本类型进行参数绑定时,就必须传入key值,且value值必须是声明的基本类型,否则将报错
2、包装类型参数绑定(推荐使用)
@RequestMapping("/integer") @ResponseBody public String getInt(Integer id){ return ""+id; }
请求:http://localhost:8080/integer?3
http://localhost:8080/integer
http://localhost:8080/integer?id=
上面的三个请求响应都是:null
请求:http://localhost:8080/integer?id=3
响应:3
结论:包装类型绑定参数时,key的值可以不传,数据也可以为空,但是要想绑定成功绑定,传的key值要和里面绑定的参数名一致
如果想要必须传入某个参数进行绑定,可以用@RequestParam,用了这个只有所需绑定的参数必须传入,否则报错
3、数组元素绑定
@RequestMapping("/array") @ResponseBody public String[] getUser(String[] name){ return name; } 请求:http://localhost:8080/user?name=xiaoshu&name=xiaozhang 响应:
["xiaoshu","xiaozhang"]
4、多层级对象的绑定
在user类中加入一个admin的对象属性
@RequestMapping("/user") @ResponseBody public User getUser(User user){ return user; }
请求:http://localhost:8080/user?name=xiaoshu&id=1&address=hangzhou&admin.name=xiaoli
响应{"id":1,"name":"xiaoshu","address":"hangzhou","admin":{"name":"xiaoli","address":null}}
结论:将参数绑定到对象内层对象的属性中,如这里例子中的加上admin.name
5、同属性对象参数绑定
这里有两个对象user和admin,它们有两个相同的属性name和address
public String list(User user, Admin admin) { return user.toString()+""+admin.toString(); } 发送请求:http://localhost:8080/list?name=xiaoshu&address=home 得到:User{id=null, name='xiaoshu', address='home'}Admin{name='xiaoshu', address='home'} 发现属性被同时绑定到了两个对象上 我们可以通过在controller中配置一个@InitBinder进行分开绑定
@InitBinder("user") public void initUser(WebDataBinder binder){ binder.setFieldDefaultPrefix("user."); } @InitBinder("admin") public void initAdmin(WebDataBinder binder){ binder.setFieldMarkerPrefix("admin."); } 此时发送请求:http://localhost:8080/list?user.name=xiaoshu&user.address=home 得到:User{id=null, name='xiaoshu', address='home'}Admin{name='null', address='null'} 可以看到带user.前缀的参数只绑定到了user对象中 那如果试一下没有配置@InitBinder时用user.和admin.作为前缀进行请求发送 请求:http://localhost:8080/list?user.name=xiaoshu&user.address=home&admin.name=xiaozhang&admin.address=hangzhou 响应:User{id=null, name='null', address='null'}Admin{name='null', address='null'} 发现都是为空,参数未能成功绑定 结论:进行同属性参数绑定时,要区分绑定时需在属性前加上对象前缀,并在controller中配置@InitBinder来明确哪个前缀的属性绑定到哪个对象中 没有配置@InitBinder加前缀不能成功绑定
6、List绑定
@RequestMapping("/list") @ResponseBody public AdminList getUser(AdminList adminList){ return adminList; }
请求:http://localhost:8080/list?admins[0].name=xiaoshu&admins[1].name=xiaozhang
响应:
{"admins":[{"name":"xiaoshu","address":null},{"name":"xiaozhang","address":null}]}
结论:这里不能直接用List去绑定,而是要创建一个类类中创建一个List去绑定,AdminList中创建了一个ArrayList。
7、Map绑定
@RequestMapping("/map") @ResponseBody public AdminMap getMap(AdminMap adminMap){ return adminMap; }
AdminMap中维护了一个Hashmap
请求:http://localhost:8080/map?admins[‘X’].name=xiaoshu&admins[‘Y’].name=xiaozhang
响应:
{"admins":{"Y":{"name":"xiaozhang","address":null},"X":{"name":"xiaoshu","address":null}}}
结论:map存放可以保证key的唯一性,过滤冲重复数据8.重点介绍表单多对象传递的绑定(特别是多对象的属性名相同的情况):
问题出现
要在一张表单中提交多个对象,并且还要在后台Controller 中精准的绑定接收。可是,这些对象中的参数名可能相同,后台接收入参时无法像struts那样jsp表单中使用Object.Param形式对表单进行精准绑定入参,我们都知道struts2默认就是这种方案,这是因为struts2采用了OGNL,并通过栈(根对象)进行操作的,而且栈中默认有action实例,所以很自然的没有这种问题。另一方面说,Struts用这种方式绑定入参,却牺牲了性能。虽然springmvc不能像struts那样方便的很直接的入参绑定,当然,spring多对象绑定入参也提供了方法。
今天就以前台表单提交两个对象做实例。为了扩大影响,我让这两个对象的属性相同。
User.Java 和Addr.java
- public class User implements Serializable{
- String id;
- String name;
- //get..set….
- }
- public class Addr implements Serializable{
- String id;
- String name;//set..get…
- }
前台JSP
- <form action=“/test/test” method=”post”>
- <input type=“text” name=”user.id” value=”huo_user_id”>
- <input type=“text” name=”user.name” value=”huo_user_name”>
- <input type=“text” name=”addr.id” value=”huo_addr_id”>
- <input type=“text” name=”addr.name” value=”huo_addr_name”>
- <input type=“submit” value=”提交”>
- </form>
若字段过多或者一个属性字段供几个对象使用,则需要前端组装处理:
$(“#submitForm”).attr(“action”, basePath + “xtapply/submitApplyASD?” + new Date().getTime())
.append($(“<input>”).attr(“name”, “creditProductId”).val(“${product.creditProductId}”).hide())
.append($(“<input>”).attr(“name”, “productName”).val(“${product.productName}”).hide())
.append($(“<input>”).attr(“name”, “bankName”).val(“${product.bankName}”).hide())
.append($(“<input>”).attr(“name”, “xtFddbr.identyNoCounty”).val($(“#identyArea option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessProvince”).val($(“#qyProvince option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessCity”).val($(“#qyCity option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessCounty”).val($(“#qyArea option:selected”).text()).hide());
$(“#submitForm”).submit();
看到这种情况,很容易想到struts进行绑定非常方便,可是,谁让我们要使用SpringMVC呢。。。这种情况springMVC直接进行入参,是不能接收到参数的。
解决思路:
使用 @InitBinder 注解进行绑定参数。前台表单中name属性仍然使用Object.Param形式传入。(另一种解决思路:扩展spring的HandlerMethodArgumentResolver以支持自定义的数据绑定方式。)
- @InitBinder(“user”)
- public void initBinderUser(WebDataBinder binder) {
- binder.setFieldDefaultPrefix(“user.”);
- }
此处使用@InitBinder() 中间的value,用于指定命令/表单属性或请求参数的名字,符合该名字的将使用此处的DataBinder,如我们的@ModelAttribute(“user1”) User user1 将使用@InitBinder(“user1″)指定的DataBinder绑定;如果不指定value值,那么所有的都将使用。
DataBinder.setFieldDefaultPrefix 意思是设置参数的前缀,如我们的是”user1.”,此处不能少了”.”,
这种方式的缺点:
1、不支持Path variable的绑定,如/test1/{user1.id}这种情况的绑定;
2、不支持如集合/数组的绑定;
后台接收 完整代码:
- @Controller
- @RequestMapping(“/test”)
- public class TestController {
- // 绑定变量名字和属性,参数封装进类
- @InitBinder(“user”)
- public void initBinderUser(WebDataBinder binder) {
- binder.setFieldDefaultPrefix(“user.”);
- }
- // 绑定变量名字和属性,参数封装进类
- @InitBinder(“addr”)
- public void initBinderAddr(WebDataBinder binder) {
- binder.setFieldDefaultPrefix(“addr.”);
- }
- @RequestMapping(“/test”)
- @ResponseBody
- public Map<String,Object> test(HttpServletRequest request,@ModelAttribute(“user”) User user,@ModelAttribute(“addr”) Addr addr){
- Map<String,Object> map=new HashMap<String,Object>();
- map.put(“user”, user);
- map.put(“addr”, addr);
- return map;
- }
②第二种方式:上面针对jsp页面表单提交前后台处理方法,若是有保存需求,因为提交后台数据的组装方式变化,在ajax中以data形式请求,则需要使用自定义的反序列化对象来实现;
var params = decodeURIComponent(
$(“#submitForm”)
.append($(“<input>”).attr(“name”, “applyId”).val($(“#applyId”).val()).hide())
.append($(“<input>”).attr(“name”, “creditProductId”).val($(“#creditProductId”).val()).hide())
.append($(“<input>”).attr(“name”, “productName”).val($(“#productName”).val()).hide())
.append($(“<input>”).attr(“name”, “bankName”).val($(“#bankName”).val()).hide())
.append($(“<input>”).attr(“name”, “xtFddbr.identyNoProvince”).val($(“#identyProvince option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtFddbr.identyNoCity”).val($(“#identyCity option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtFddbr.identyNoCounty”).val($(“#identyArea option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessProvince”).val($(“#qyProvince option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessCity”).val($(“#qyCity option:selected”).text()).hide())
.append($(“<input>”).attr(“name”, “xtQyxx.companyBusinessCounty”).val($(“#qyArea option:selected”).text()).hide())
.serialize().replace(/\+/g,” “)
);
ajax中data组装:
msg:util.formToJson(params)//util.formToJson(decodeURIComponent($(“#submitForm”).serialize().replace(/\+/g,” “)))
后台处理:
调用工具类将json字符串安装自定义的反序列化格式转化为对象(XtyhApplyDeserializer为自定义的反序列化对象)
xtApply = jsonUtil.jsonStr2ObjectByCustom(msg, XtApply.class, new XtyhApplyDeserializer());
如上所示为一个完整的自定义反序列化类,在Controller中接收时处理如下:
注:由此完成了提交与保存两个按钮的数据保存工作(两种处理方式),保存则用到自定义的反序列化类类实现。
另外可以通过DataBinder完成如下几件事情:
1、binder.setAllowedFields(“id”) : 设置允许的字段,比如我只想设置id,那么可以调用这个方法,那么其他属性会忽略;
2、binder.setDisallowedFields(“id”) : 设置不允许的自动,比如我不想设置id,那么可以调用此方法,这个属性将不设置;
3、binder.setRequiredFields() : 表示哪些字段是必填的;
4、binder.setValidator() :设置自定义的验证器,如果如JSR-303不适合,可以使用这个。
注意:1.如果多个对象的属性相同,且没有指定前缀的话,会都匹配或者出错;
转载请注明:SuperIT » springmvc中的数据绑定(前后台传递)