SpringMVC数据绑定梳理

it2025-03-13  22

学习目标:

SpringMVC数据绑定梳理;


学习内容:

主要学习资料:慕课网:geely老师的课程《SpringMVC数据绑定入门》 目录: 1、基本数据类型和封装数据类型 2、简单对象和复杂对象 3、集合类对象 4、json && xml 5、PropertyEditor 、Formatter、 Converter


学习产出:

1、基本数据类型:learnInt.htm?count=123123

// Fixme 基本数据类型 learnInt.htm?count=0 @RequestMapping("/learnInt") @ResponseBody public String learnInt(int count) { return "leanInt out :" + count; }

leanInt out :123123 必须传递值, 否则异常,异常原因:java.lang.IllegalStateException: Optional int parameter ‘count’ is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.

2、封装类型: learnInteger.htm?count=1323

// Fixme 封装类型 learnInteger.htm?count=0 @RequestMapping("/learnInteger") @ResponseBody public String learnInteger(/*@RequestParam(required = true)*/ Integer count) { return "learnInteger out:" + count; }

learnInteger out:1323 可以不传count, 除非被设置@RequestParam(required = true)

3、数组对象: learnArray.htm?names=tom&names=jetty

@RequestMapping("learnArray") @ResponseBody public String learnArray(String[] names) { StringBuilder stringBuilder = new StringBuilder(); Stream.of(names).forEach( e -> { stringBuilder.append(e).append(" "); } ); return stringBuilder.toString(); }

tom jetty

4、简单对象: learnUser.htm?name=tom

// Fixme 简单对象的嵌套 learnUser.htm?name=tom @RequestMapping("/learnUser") @ResponseBody public String learnUser(User user) { return user.toString(); }

User{name=‘tom’, age=‘null’, contact=null}

5、嵌套对象: learnUser.htm?name=tom&pwd=12122&contact.email=erere

// fixme learnUser.htm?name=tom&age=12122&contact.email=erere @RequestMapping("/learnUser") @ResponseBody public String learnUser(User user) { return user.toString(); }

User{name=‘tom’, age=‘null’, contact=Contact{email=‘erere’, phone=‘null’}}

6、嵌套对象: 6.1:多个对象不存在相同名称的属性:learnUser2.htm?name=tom&age=12122&name2=21&age2=12

@RequestMapping("/learnUser2") @ResponseBody public String learnUser2(User user, User2 user2) { return user.toString()+user2.toString(); }

User{name=‘tom’, age=‘12122’, contact=null}User2{name2=‘21’, age2=‘12’}

6.2:多个对象存在相同的属性名称learnUserAndAdmin.htmuser.name=tom&admin.name=jetty

// Fixme 多个简单对象中存在多个同名的参数 learnUserAndAdmin.htm?user.name=tom&admin.name=jetty @RequestMapping("/learnUserAndAdmin") @ResponseBody public String learnUserAndAdmin(User user, Admin admin) { return "user:" + user.toString() + " admin:" + admin.toString(); } @InitBinder("user") public void initUser(WebDataBinder binder) { binder.setFieldDefaultPrefix("user."); } @InitBinder("admin") public void initAdmin(WebDataBinder binder) { binder.setFieldDefaultPrefix("admin."); }

user:User{name=‘tom’, age=‘null’, contact=null} admin:Admin{name=‘jetty’, age=‘null’}

在这里借用了 @InitBinder 和 WebDataBinder, initBinder的作用域局限于当前controller, 且只对指定开头的字符有作用

7、集合对象: 7.1:List:learnList.htm?users[0].name=tom&users[3].name=jetty

// fixme 这里用ListForm 其实就是用List构建了一个bean, 他的作用是为List提供 get&set 方法 , 这是在参数转化过程中必须要用到的; @RequestMapping("/learnList") @ResponseBody public String leanList(ListForm list) { return "learn list: " + list.toString(); } public class ListForm { private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } @Override public String toString() { return "ListForm{" + "users=" + users + '}'; } }

这里用ListForm 其实就是用List构建了一个bean, 他的作用是为List提供 get&set 方法 , 这是在参数转化过程中必须要用到的;

learn list: ListForm{users=[User{name=‘tom’, age=‘null’, contact=null}, User{name=‘null’, age=‘null’, contact=null}, User{name=‘null’, age=‘null’, contact=null}, User{name=‘jetty’, age=‘null’, contact=null}]}

List 如果入参存在跨度如:users[0].name=tom&users[3].name=jetty 没有 users[2] 但是在输出结果中还是生成了, 所以会生成空对象;

7.2:Set:learnSet.htm?users[0].name=tom

// fixme 这里用ListForm 其实就是用Set构建了一个bean, 他的作用是为Set提供 get&set 方法 , 这是在参数转化过程中必须要用到的; @ResponseBody @RequestMapping("/learnSet") public String learnSet(SetForm setForm) { return setForm.toString(); } public class SetForm { private HashSet<User> users; //fixme 初始化hashSet, 如果这里不初始化会报错, 但实际上这样也会报错,因为这里只是初始化了一个user, 如果(肯定)入参会是多个User, 在代码中是无法预测的, 所以对set的支持不友好; { User user = new User(); users = new HashSet<>(); users.add(user); } public HashSet<User> getUsers() { return users; } public void setUsers(HashSet<User> users) { this.users = users; } @Override public String toString() { return "SetForm{" + "users=" + users + '}'; } }

SetForm{users=[User{name=‘tom’, age=‘null’, contact=null}]} 这里使用SetForm, 其原理同上面的ListForm

//fixme 初始化hashSet, 如果这里不初始化会报错, 但实际上这样也会报错,因为这里只是初始化了一个user, 如果(肯定)入参会是多个User, 在代码中是无法预测的, 所以对set的支持不友好; { User user = new User(); users = new HashSet<>(); users.add(user); }

7.3:Map:learnMap.htm?users[tom].name=tom

@ResponseBody @RequestMapping("/learnMap") public String learnMap(MapForm mapForm) { return mapForm.toString(); } public class MapForm { private HashMap<String, User> users; public HashMap<String, User> getUsers() { return users; } public void setUsers(HashMap<String, User> users) { this.users = users; } @Override public String toString() { return "MapForm{" + "users=" + users + '}'; } }

MapForm{users={tom=User{name=‘tom’, age=‘null’, contact=null}}} 使用MapForm 作用同上ListForm,SetForm

8、json & xml:

8.1:Json: POST:learnJson.htm Content-Type:application/json { "name":"tom", "age":11 } @RequestMapping("/learnJson") @ResponseBody public String learnJson(@RequestBody User user) { return user.toString(); }

User{name=‘tom’, age=‘11’, contact=null}

使用 @RequestBody 接收请求的数据流

如果有异常尝试添加:

<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> 8.1:Xml: POST:learnXml.htm

Content-Type:text/xml

<?xml version="1.0" encoding="UTF-8"?> <admin> <name>jetty</name> <age>12</age> </admin> @RequestMapping("/learnXml") @ResponseBody public String learnXml(@RequestBody Admin admin) { return admin.toString(); } @XmlRootElement(name = "admin") public class Admin { private String name; private String age; @XmlElement(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "age") public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "Admin{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; }

Admin{name=‘jetty’, age=‘12’}

使用@RequestBody接收请求的数据流

使用 @XmlRootElement @XmlElement 解析接收的xml文件

9、PropertyEditor: 局部使用 learnPropertyEdit.htm?manager.date=2020-10-12

public class MyPropertyEditor extends PropertyEditorSupport { private SimpleDateFormat simpleDateFormat; public void setFormatterString(String formatterString) { this.simpleDateFormat = new SimpleDateFormat(formatterString); } @Override public void setAsText(String text) throws IllegalArgumentException { try { Date parse = simpleDateFormat.parse(text); setValue(parse); } catch (ParseException e) { e.printStackTrace(); } } @Override public String getAsText() { if (getValue() instanceof Date) { Date value = (Date) getValue(); return simpleDateFormat.format(value); } return super.getAsText(); } } @RequestMapping("/learnPropertyEdit") @ResponseBody public String leanPropertyEdit(Manager manager) { return manager.toString(); } @InitBinder("manager") public void initManager(WebDataBinder binder) { binder.setFieldDefaultPrefix("manager."); MyPropertyEditor myPropertyEditor = new MyPropertyEditor(); myPropertyEditor.setFormatterString("yyyy-MM-dd"); binder.registerCustomEditor(Date.class, myPropertyEditor); }

Manager{date=Mon Oct 12 00:00:00 CST 2020}

将String 转化成某种数据格式

10、Formatter: 全局/局部 leanFormatter.htm?form=form.root@12345

public class MyFormatter implements Formatter<Form> { private static final String PREFIX = "form."; @Override public Form parse(String text, Locale locale) { Form form = new Form(); if (!StringUtils.isEmpty(text) && text.startsWith(PREFIX)) { String substring = text.substring(5); String[] split = substring.split("@"); form.setName(split[0]); form.setPwd(split[1]); form.setMsg(text); } return form; } @Override public String print(Form object, Locale locale) { return object.toString(); } @RequestMapping("/leanFormatter") @ResponseBody public String learnFormatter(Form form) { return form.toString(); } public class Form { private String msg; private String name; private String pwd; } <mvc:annotation-driven conversion-service="myFormFormatter"/> <bean id="myFormFormatter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="formatters"> <set> <bean class="com.mtn.mvccontroller.common.MyFormatter"/> </set> </property> </bean>

Form{msg=‘form.root@12345’, name=‘root’, pwd=‘12345’}

将String转化为相应实体, 可全局注册, 也可以通过@InitBinder局部注册

11、Converter: 全局/局部 learnConvert.htm?roots=boss;manager

@RequestMapping("/learnConvert") @ResponseBody public String learnConvert(@ModelAttribute("roots") HashSet<Root> roots) { return roots.toString(); } public class MyConvert implements Converter<String, HashSet<Root>> { @Override public HashSet<Root> convert(String value) { HashSet<Root> roots = new HashSet<>(); if (!StringUtils.isEmpty(value)) { String[] split = value.split(";"); Stream.of(split).forEach(e -> { Root root = new Root(); root.setRole(e); roots.add(root); }); } return roots; } } <bean id="myFormFormatter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.mtn.mvccontroller.common.MyConvert"/> </set> </property> </bean>

[Root{role=‘boss’}, Root{role=‘manager’}]

Converter的转化类型是可以自定义的;

总结:

spring中默认的参数转化器有近100种, 如果默认的不满足需求的话, 可以自己写 PropertyEditor、Formatter、 Converter去转化, 例如上面 Converter的案例, spring本身对hashSet转化支持不是特别良好, 可以使用Converter满足开发需求;

最新回复(0)