SpringMVC数据绑定梳理

本文详细梳理了SpringMVC中的数据绑定,包括基本数据类型、封装类型、数组对象、简单对象、嵌套对象、集合类对象(List、Set、Map)、JSON与XML的处理,以及PropertyEditor、Formatter和Converter的使用。通过实例展示了如何进行参数转化,并指出Spring对HashSet的转化支持不足,可通过Converter自定义转化来解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习目标:

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满足开发需求;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值