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. VSC 创建 Net Core 3.0 版本 WebAPI

    首先确保安装好了VSC(这个算是废话......), 并且为VSC安装了Microsoft提供的C#插件, 以及.Net Core 3.0, 然后就可以开始了 1. 使用VSC打开已经创建好的文件夹( ...

  2. centos7 laravel 项目 npm install报错

    npm install 初始化项目依赖的前端资源   报错 ERR xxx .. socket,symbol link is not supported ... 如果报错了 重新npm install ...

  3. Android 项目优化(一):项目代码规范优化

    项目代码规范为主要包含:类,常量,变量,ID等命名规范:注释规范:分包规范:代码风格规范. 项目代码规范是软件开发过程中非常重要的优化环节. 目前的开发社区提供了很多的开发规范文档,阿里巴巴推出了&l ...

  4. diango中让装了装饰器的函数的名字不是inner,而是原来的名字

    让装了装饰器的函数的名字不是inner,而是原来的名字 from functools import wraps def wrapper(func): @wraps(func) # 复制了原来函数的名字 ...

  5. [洛谷P1169][题解][ZJOI2007]棋盘制作

    我不是题目的说 这道题运用了一种很巧妙的DP方式:悬线法 如图,蓝色为悬线,黄色为向两边延伸的长度 那么显然,最大子矩形的宽一定是这些黄线中最小的(证明从略) 所以我们可以维护三个数组: Up[i][ ...

  6. Network出现两次相同请求?

    出现的状况 Network中出现了两个相同的请求(如图),两个发起了同样的请求,花的时间却不同,一个55ms,一个花了294ms.   两个相同的请求 什么情况啊?研究了一番,我发现有一个地方是不同的 ...

  7. 使用python - selenium模拟登陆b站

    思路 输入用户名密码点击登陆 获取验证码的原始图片与有缺口的图片 找出两张图片的缺口起始处 拖动碎片 功能代码段 # 使用到的库 from selenium import webdriver from ...

  8. java遍历request.getParameterMap()中的值

    在开发过程中发现request对象有提供一个request.getParameterMap()方法可以获取到从前端请求发送的参数Map. 但是在使用get()方法通过key(键)去获取这个参数Map中 ...

  9. Java题库——Chapter2 基础程序设计

    1)Suppose a Scanner object is created as follows: Scanner input = new Scanner(System.in); What metho ...

  10. cocos2d-x 新工程的把玩

    创建了cocos的工程以及初步了解了工程的结构之后,可以尝试自己改改代码了 游戏窗口的设置 首先是AppDelegate,找到AppDelegate.cpp中AppDelegate::applicat ...