学习目标:
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.htmContent-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满足开发需求;