Spring MVC 之数据绑定

数据绑定是将用户输入绑定到领域模型的一种特性。

Http 请求传递的数据为 String 类型,通过数据绑定,可以将数据填充为不同类型的对象属性。

基本类型绑定

@RequestMapping("/int")
@ResponseBody
public String bindInt (int i) {
return "bindInt:" + i;
}

请求:/int?i=10

响应:bindInt:3

错误请求:/int or /int?2

错误代码:500

基本类型数据绑定时,必须传入keyvalue

包装类型绑定

@RequestMapping("/integer")
@ResponseBody
public String bindInteger (Integer i) {
return "bindInteger:" + i;
}

请求:/integer?i=5

响应:bindInteger:5

请求:/integer or /integer?5 or /integer?i or /integer?i=

响应:bindInteger:null

包装类型数据绑定时,keyvalue均可为空。

若要求必须传入参数,可加@RequestParam

@RequestMapping("/integer")
@ResponseBody
public String bindInteger (@RequestParam Integer i) {
return "bindInteger:" + i;
}

错误请求:/integer or /integer?5

错误代码:400

请求:/integer?i or /integer?i=

响应:bindInteger:null

数组绑定

@RequestMapping("/array")
@ResponseBody
public String bindArray (String[] strs) {
StringBuilder sb = new StringBuilder();
sb.append("bindArray:");
for (String str : strs)
sb.append(str + " ");
return sb.toString();
}

请求:/array?strs=str1&strs=str2

响应:bindArray:str1 str2

简单对象绑定

@RequestMapping("/user")
@ResponseBody
public String bindUser (User user) {
return "bindUser:" + user.toString();
}
public class User {
private String name;
private Integer age;
// getters and setters
// toString
}

请求:/user?name=Tom&age=15

响应:bindUser:User [name=Tom, age=15]

多层级对象绑定

public class User {
private String name;
private Integer age;
private ContactInfo info;
// getters and setters
// toString
}
public class ContactInfo {
private String phone;
private String address;
// getters and setters
// toString
}

请求:/user?name=Tom&info.phone=123456

响应:bindUser:User [name=Tom, age=null, info=ContactInfo [phone=123456, address=null]]

同属性多对象绑定

@RequestMapping("/userandadmin")
@ResponseBody
public String bindUserAndAdmin (User user, Admin admin) {
return "bindUserAndAdmin:\n" + user.toString() + "\n" + admin.toString();
}
public class Admin {
private String name;
private Integer age;
// getters and setters
// toString
}

请求:/userandadmin?name=Tom&age=15

响应:

bindUserAndAdmin:

User [name=Tom, age=15]

Admin [name=Tom, age=15]

数据同时被绑定到了具有相同属性的两个对象上。

解决方式:

@InitBinder("user")
public void initUser(WebDataBinder binder) {
binder.setFieldDefaultPrefix("user.");
} @InitBinder("admin")
public void initAdmin(WebDataBinder binder) {
binder.setFieldDefaultPrefix("admin.");
}

请求:/userandadmin?user.name=Tom&age=15&admin.name=Jack

响应:

bindUserAndAdmin:

User [name=Tom, age=15]

Admin [name=Jack, age=15]

InitBinder作用在该控制器中,进入控制器后先调用InitBinder后调用RequestMapping方法。

不配置InitBinder加前缀无效,不能成功绑定。

WebDataBinder用来绑定请求参数到指定JavaBean

List 绑定

@RequestMapping("/stringlist")
@ResponseBody
public String bindStringList (@RequestParam("name") List<String> strs) {
return "bindStringList:" + strs.toString();
}

请求:/stringlist?name=Tom&name=Jack

响应:bindStringList:[Tom, Jack]

@RequestMapping("/userlist")
@ResponseBody
public String bindUserList(UserListForm userListForm){
return "bindUserList:\n" + usersModel.toString();
}
public class UserListForm {
private List<User> list;
// getters and setters
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (User u : users)
sb.append(u.toString() + "\n");
return sb.toString();
}
}

请求:/userlist?users[0].name=Tom&users[2].name=Jack

响应:

bindList:

User [name=Tom, age=null]

User [name=null, age=null]

User [name=Jack, age=null]

PS:tomcat 高版本中[需要转义为%5B]需要转义为%5D

Set 绑定

Set 与 List 绑定基本一致,但在使用Set<User>时,需先初始化Set的容量。

public class UserSetForm {
private Set<User> users;
private UserSetForm () {
// 设置 set 容量为 2 不能访问 size 外的对象
// 如果覆盖了 User 的 equals 和 hashcode 方法 则 set 容量为 1
users = new HashSet<User>();
users.add(new User());
users.add(new User());
System.out.println(users.size());
}
// getters and setters
// toString
}
// User.java
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

请求:/userset?users[0].name=Tom

响应:

bindUserSet:

User [name=Tom, age=null]

错误请求:/userset?users[0].name=Tom&users[1].name=Jack

错误代码:500

错误说明:Cannot get element with index 1 from Set of size 1, accessed using property path 'users[1]'

Map 绑定

@RequestMapping("/usermap")
@ResponseBody
public String bindUserMap (UserMapForm userMapForm) {
return "bindUserMap:\n" + userMapForm.toString();
}
public class UserMapForm {
private Map<String, User> users;
// getters and setters
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (String s : users.keySet())
sb.append("Users[" + s + "] = " + users.get(s) + "\n");
return sb.toString();
}
}

请求:/usermap?users[x].name=Tom&users[y].name=Jack

响应:

bindUserMap:

Users[x] = User [name=Tom, age=null]

Users[y] = User [name=Jack, age=null]

JSON 绑定

@RequestMapping("/userjson")
@ResponseBody
public String bindUserJson (@RequestBody User user) {
return "bindUserJson:" + user.toString();
}

请求:

Content-Type:application/json

Body:

{
"name":"Tom",
"age":15
}

响应:bindUserJson:User [name=Tom, age=15]

依赖:jackson-databind

XML 绑定

@RequestMapping("/userxml")
@ResponseBody
public String bindUserXml (@RequestBody User user) {
return "bindUserXml:" + user.toString();
}
@XmlRootElement(name="user")
public class User {
private String name;
private Integer age;
@XmlElement(name="name")
public String getName() {
return name;
}
@XmlElement(name="age")
public Integer getAge() {
return age;
}
// setters
// toString
}

请求:

Content-Type:application/xml

Body:

<user>
<name>Tom</name>
<age>15</age>
</user>

响应:bindUserXml:User [name=Tom, age=15]

依赖:jaxb-api jaxb-impl spring-oxm

PropertyEditor

public interface PropertyEditor {
void setValue(Object value);
Object getValue();
String getAsText();
void setAsText(String text) throws java.lang.IllegalArgumentException;
// ...
}

使用:一般使用内置或继承PropertyEditorSupport(implements PropertyEditor),配合WebDataBinder局部使用。

@RequestMapping("/datepe")
@ResponseBody
public String bindDateByPropertyEditor (Date date1) {
return date1.toString();
} @InitBinder("date1")
public void initDate (WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}

请求:/datepe?date1=2019-11-11

响应:Mon Nov 11 00:00:00 CST 2019

Formatter

public interface Formatter<T> extends Printer<T>, Parser<T> {

}
public interface Printer<T> {
String print(T object, Locale locale);
}
public interface Parser<T> {
T parse(String text, Locale locale) throws ParseException;
}

使用:Source 为 String 类型,可全局或局部使用。

@RequestMapping("/dateformatter")
@ResponseBody
public String bindDateByFormatter (Date date2) {
return date2.toString();
}
<mvc:annotation-driven conversion-service="formatter"/>
<!-- conversion-service 与 id 对应 -->
<bean id="formatter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="org.springframework.format.datetime.DateFormatter">
<constructor-arg name="pattern" value="yyyy-MM-dd" />
</bean>
</set>
</property>
</bean>

请求:/dateformatter?date2=2019-12-12

响应:Thu Dec 12 00:00:00 CST 2019

Converter

public interface Converter<S, T> {
@Nullable
T convert(S source);
}

使用:内置实现为 final 类,不可扩展,可自定义源类型和目的类型,可全局或局部使用。

@RequestMapping("/dateconverter")
@ResponseBody
public String bindDateByConverter (Date date3) {
return date3.toString();
}
public class DateConverter implements Converter<String, Date> {
public Date convert(String source) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
return format.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
<mvc:annotation-driven conversion-service="converter"/>
<!-- 自定义 Converter 类 -->
<bean id="converter" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="common.DateConverter" />
</set>
</property>
</bean>

请求:/dateconverter?date3=2019-10-24

响应:Thu Oct 24 00:00:00 CST 2019

参考资料:SpringMVC数据绑定入门

Java Web 学习(5) —— Spring MVC 之数据绑定的更多相关文章

  1. Java Web 学习(4) —— Spring MVC 概览

    Spring MVC 概览 一. Spring MVC Spring MVC 是一个包含了 Dispatcher Servlet 的 MVC 框架. Dispatcher Servlet 实现了 : ...

  2. Java Web 学习(7) —— Spring MVC 之国际化

    Spring MVC 之国际化 i18n 与 l10n internationalization:国际化,以 i 开头,以 n 结尾,中间 18 个字母,简称 i18n. localization:本 ...

  3. Java Web 学习(8) —— Spring MVC 之文件上传与下载

    Spring MVC 之文件上传与下载 上传文件 表单: <form action="upload" enctype="multipart/form-data&qu ...

  4. Java Web 学习(6) —— Spring MVC 之校验器

    Spring MVC 之校验器 数据验证 一个典型的 Spring MVC 应用会同时应用到 formatters/converters 和 validators. 在调用 controller 期间 ...

  5. Java Web系列:Spring MVC基础

    1.Web MVC基础 MVC的本质是表现层模式,我们以视图模型为中心,将视图和控制器分离出来.就如同分层模式一样,我们以业务逻辑为中心,把表现层和数据访问层代码分离出来是一样的方法.框架只能在技术层 ...

  6. 【Java Web开发学习】Spring MVC 使用HTTP信息转换器

    [Java Web开发学习]Spring MVC 使用HTTP信息转换器 转载:https://www.cnblogs.com/yangchongxing/p/10186429.html @Respo ...

  7. 【Java Web开发学习】Spring MVC添加自定义Servlet、Filter、Listener

    [Java Web开发学习]Spring MVC添加自定义Servlet.Filter.Listener 转载:https://www.cnblogs.com/yangchongxing/p/9968 ...

  8. 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor

    [Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...

  9. 【Java Web开发学习】Spring MVC文件上传

    [Java Web开发学习]Spring MVC文件上传 转载:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上传有两种实现方式,都比较 ...

随机推荐

  1. Audit Object Changes 审核对象更改

    Important 重要 The Audit Trail module is not supported by the Entity Framework ORM in the current vers ...

  2. Html5 小游戏 俄罗斯方块

    导言 在一个风和日丽的一天,看完了疯狂HTML 5+CSS 3+JavaScript讲义,跟着做了书里最后一章的俄罗斯方块小游戏,并做了一些改进,作为自己前端学习的第一站. 游戏效果: 制作思路 因为 ...

  3. python快速导出sql语句(mssql)的查询结果到Excel,解决SSMS无法加载大字段的问题

    遇到一个尴尬的问题,SSMS的GridView对于大字段的(varchar(max),text之类的),支持不太友好的,超过8000个长度之外的字符,SSMS的表格是显示不出来的(当然也就看不到了), ...

  4. Appium新版本遇到的问题,不能通过 name 去定位元素抛 Message: Locator Strategy 'name' is not supported for this session

    环境: 1.Appium: 1.15.1 2.Python: 3.7.0 3.Selenium: 3.141.0 4.IDE: Pycharm 5.PC:Windows 10 问题:在 Pycharm ...

  5. robotframework框架 - 在Pycharm当中编写RobotFramework测试用例

    众所周知,pycharm是个写python极好用的编辑器.也可以装很多的插件来完成各种骚操作. 某一天,心血来潮在pycharm的插件库里,搜索了一下robot,恩,发现有支持robotframewo ...

  6. nginx(4)

    目录 一.安装配置 1.安装 2.配置文件 3.测试和启动 二.功能 1.虚拟主机 1.1 基于IP 1.2 基于域名 1.3 基于端口 2.访问控制 3.用户认证 4.文件共享 5.文件别名 6.状 ...

  7. 关于String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

    关于 String path = request.getContextPath(); String basePath = request.getScheme()+"://"+req ...

  8. js中call、apply、bind到底有什么区别?bind返回的方法还能修改this指向吗?

     壹 ❀ 引 同事最近在看angularjs源码,被源码中各种bind,apply弄的晕头转向:于是他问我,你知道apply,call与bind的区别吗?我说apply与call是函数应用,指定thi ...

  9. git中报错---fatal: pathspec 'readme.txt' did not match any files

    1.git安装 git官网下载最新版本,一键安装或custom install. 2.会弹出一个类似的命令窗口的东西,就说明Git安装成功. 3.安装完成后,还需要最后一步设置,在命令行输入如下--- ...

  10. idea中导入别人的vue项目并运行

    1. 下载node.js 在搭建vue的开发环境之前,先下载node.js,下载地址:https://nodejs.org/en/ https://blog.csdn.net/antma/articl ...