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. ASP.Net 设置 404错误跳转到指定页面

    分享 ASP.Net 网站设置 404错误跳转到指定页面的三种方法 方法一:Web.config 配置 1 首先双击打开项目中的“Web.config”文件  找到 system.web 节点,在 c ...

  2. pandas 初识(五)

    1. 如何实现把一个属性(列)拆分成多列,产生pivot,形成向量信息,计算相关性? 例: class_ timestamp count 0 10 2019-01-20 13:23:00 1 1 10 ...

  3. Lucene&Solr框架之第一篇

    2.信息检索 信息检索是计算机世界中非常重要的一种功能.信息检索不仅仅是指从数据库检索数据,还包括从文件.网页.邮件.用户手输入的内容中检索数据.通过怎样的高效方式将用户想要的信息快速提取出来,是计算 ...

  4. 怎么将DWG转PDF?分享一个在线转换方法

    了解CAD的朋友们都知道,在使用CAD制图软件绘制图纸的时候,默认的CAD图纸保存格式就是为DWG格式.但是DWG格式的文件不能够直接进行打开查看,就需要将DWG转PDF格式.那具体要怎么来进行操作呢 ...

  5. [转]UiPath State Machines

    本文转自:https://docs.uipath.com/studio/docs/state-machines A state machine is a type of automation that ...

  6. Android 网络交互之移动端与服务端的加密处理

    在开发项目的网络模块时,我们为了保证客户端(Client)和服务端(Server)之间的通信安全,我们会对数据进行加密. 谈到网络通信加密,我们可以说出:对称加密,非对称加密,md5单向加密,也能提到 ...

  7. Redis内存数据库在Exchange会议室的整体应用架构

    注:本文是别人写的,感觉写得很好就转过来,版权归原作者所有哦,谁知道出处可以告诉我,谢谢. 根据以上的会议室应用现状分析,该架构的核心是把历史发生的会议室申请数据定时同步到Redis内存数据库中,对于 ...

  8. [译]Vulkan教程(09)窗口表面

    [译]Vulkan教程(09)窗口表面 Since Vulkan is a platform agnostic API, it can not interface directly with the ...

  9. angularjs路由监听,uirouter感知路由变化,解决uirouter路由监听不生效的问题

     壹 ❀ 引 angularjs除了惊为天人的双向数据绑定外,路由也是出彩的一笔,通过路由配置,我们能在不发起页面跳转的情况下,对当前页内容进行整体更新,angularjs提供了ngRoute模块用于 ...

  10. [译]OpenSSL Cookbook

    记录个人学习过程吧,顺便翻译一下.另外,本文并不会包括原连接中的所有内容,仅包括个人在工作中会经常遇到的. 参考:OpenSSL Cookbook 前言 由于协议特性和实现的复杂性,有时很难确定安全服 ...