目录
一、拾枝杂谈
0.为什么需要数据格式化?
在 Web 开发中,我们面临一个核心矛盾:HTTP 协议是基于文本(字符串)的,而 Java 语言是强类型的;也就是说,当用户在表单里输入"18"或"2026-05-20"时,发送到后端的其实全是 字符串。
那么问题来了,如何将这些原始字符串"丝滑"地转换为后端 Controller 能够直接使用的 Integer、Double 甚至 Date 对象?这就是 数据格式化与转换 机制要解决的问题。
1.什么是数据格式化?
数据格式化(Data Formatting) 在 SpringMVC 中被定义为一种特殊的 "类型转换"。它不仅仅是把 A 类型变成 B 类型,更重要的是它关注数据的表现形式(Format)。
数据格式化主要处理 "字符串" 与 "复杂 Java 类型(如日期、货币、大数字)"之间的双向转换。例如:
(1) ++日期/时间格式化++:将 "2026/05/20" 或 "2026-05-20" 转换为 java.util.Date。
(2)++数值/货币格式化++:将 "¥1,234.56" 转换为 Float 或 BigDecimal。
(3) ++区域化处理++:根据用户所在的地区(Locale),自动识别不同的数字或日期分隔符。
2.如何实现数据转换?
SpringMVC 并没有为每种类型 硬编码 转换逻辑,而是设计了一套极其灵活的 "转换"体系。具体如下------
- 转换器(Converter):Spring 内部定义了成百上千个具体的转换器,比如 StringToStringConverter、StringToNumberConverterFactory 等。
- 统一接口------ConversionService : 这是 Spring 类型转换体系的核心入口。无论底层有多少个具体的转换器,它们最终都会被注入并注册到 ConversionService 这个接口的实现类中(通常是 DefaultFormattingConversionService)。
- 工作原理 :当 DataBinder(数据绑定器)发现需要将请求参数赋值给 Java 对象时,它会向 ConversionService 发出请求,就像说:"我这里有个字符串,你能帮我把它转成这个字段对应的类型吗?"。随后,ConversionService 就会在内部寻找最合适的转换器并完成任务。
3.基本类型的自动转换:
对于 Java 中的基本 数据类型(及其包装类),SpringMVC 提供了 "开箱即用" 的自动转换支持;只要我们的项目中配置了 <mvc:annotation-driven />,Spring 就会自动在后台初始化这些基本类型的转换器,无需任何额外配置。
- 数值型:只要字符串是由纯数字组成的(如 "123"),Spring 就能自动将其转为 int、long、double 等。
- 布尔型:字符串 "true"、"false" 都可以被自动识别并映射为 boolean。
4.特殊类型的自动转换:
当涉及到日期、货币等"格式多变"的特殊类型时,简单的自动识别就不灵了,因为 Spring 不知道你的日期是用 斜杠 / 还是 横杠 - 分隔的。
实现机制 :SpringMVC 通过在 POJO(JavaBean)的属性上添加 格式化注解,来告知转换器具体的解析规则。具体如下------
- @DateTimeFormat: 用于处理日期和时间类型,其中最常用的属性是 pattern。
- 示例:@DateTimeFormat(pattern = "yyyy-MM-dd"),它相当于告诉 Spring:"如果看到类似 2026-05-20 格式的字符串,就按这个模板解析成日期对象。"
- @NumberFormat: 用于处理数字、货币或百分比。
- 示例:@NumberFormat(pattern = "#,###.##")。它可以处理带 逗号 分隔符或 指定小数位数 的数字字符串。
二、应用实例
1.需求:
**(1)**以 Student 类为 POJO类,对应的 Handler 为 StudentHandler;
**(2)**在 web 目录下新建一个 data_format.jsp 页面,用来发出GET 请求到 StudentHandler;
**(3)**StudentHandler 会进行转发,转发到一个 addStudent页面;
**(4)**在 addStudent 页面下,重新发出 POST 请求,打到 StudentHandler,后者要处理该次请求,并验证模型数据是否成功封装。(在模型数据自动封装的过程中,对于 基本类型,会进行自动数据转换)
(5)要验证特殊类型转换,需要用到特殊的注解,具体演示请移步下文。
2.环境搭建:
先准备好 Student 类以及它对应的 Handler,如下图所示:

Student 类代码如下:
java
package com.cyan.web.dataformatting.entity;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
public class Student {
private int id;
private int age;
private String name;
private String email;
public Student() {
}
public Student(int id, int age, String name, String email) {
this.id = id;
this.age = age;
this.name = name;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
StudentHandler 类代码如下:
java
package com.cyan.web.dataformatting.handler;
import com.cyan.web.dataformatting.entity.Student;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.Map;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
@Controller(value = "student01")
@Scope(value = "prototype")
public class StudentHandler {
/**
* @param map: Map 中的数据会自动 放入 request 域.
* @return: 这个方法 会转发到 添加学生的 页面
*/
@GetMapping(value = "/forwardAdd")
public String forwardAdd(Map<String, Object> map) {
map.put("student", new Student());
return "dataformat/addStudent";
}
@PostMapping(value = "/addStudent")
public String addStudent(Student student) {
System.out.println("student = " + student);
return "dataformat/success";
}
}
接着准备 第一个 页面,data_format.jsp 代码如下:
html
<%--
User : Cyan_RA9
Version : 24.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>数据格式化</title>
</head>
<body>
<h2>新增学生</h2>
<hr>
<a href="<%= request.getContextPath()%>/forwardAdd">点我添加学生</a>
</body>
</html>
因为请求转发 要用到SpringMVC 的默认视图解析器,所以要把 转发的页面放在 pages目录下,如下图所示:

第二个页面 addStudent.jsp 代码如下:
html
<%--
引入 Spring 自己的 form 标签.
--%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加学生</title>
</head>
<body>
<%--
使用 SpringMVC 自己的 form 标签, 方便提示信息的回显.
1) SpringMVC form 标签在回显之前必须在 request域 中有一个bean 对象,
这个 bean 对象在 request 域中对应的 key 就是 modelAttribute 属性的值.
--%>
<form:form action="addStudent" method="post" modelAttribute="student">
<table style="border: 2px cornflowerblue solid">
<tr rowspan="2">
<th style="font-weight: bold">添加学生的表单</th>
</tr>
<tr>
<td>学生id : </td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>学生age:</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td>学生name:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>学生e-mail:</td>
<td><input type="text" name="email"></td>
</tr>
<tr>
<td><input type="submit" value="SUBMIT"/></td>
<td><input type="reset" value="RESET"/></td>
</tr>
</table>
</form:form>
</body>
</html>
第三个页面 success.jsp 代码如下:
html
<%--
User : Cyan_RA9
Version : 24.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>原神牛逼</title>
</head>
<body>
<h2>Operation Successful!</h2>
</body>
</html>
3.基本格式类型字符串自动转换测试:
运行结果如下 GIF图所示:

可以看到,学生的 id, age 都由表单的 String 自动转换成了 int 类型。
4.特殊格式类型和字符串自动转换测试:
在 Student 类中新增两个字段 birthday 和 annualBalance,分别用 @DateTimeFormat 和 @NumberFormat 修饰。调整 Student 类对应的 getter, setter 方法 和 toString 方法,Student 类代码如下:
java
package com.cyan.web.dataformatting.entity;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import java.util.Date;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
public class Student {
private int id;
private int age;
private String name;
private String email;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern = "###,###.##")
private Float annualBalance;
public Student() {
}
public Student(int id, int age, String name, String email, Date birthday, Float annualBalance) {
this.id = id;
this.age = age;
this.name = name;
this.email = email;
this.birthday = birthday;
this.annualBalance = annualBalance;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Float getAnnualBalance() {
return annualBalance;
}
public void setAnnualBalance(Float annualBalance) {
this.annualBalance = annualBalance;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birthday=" + birthday +
", annualBalance=" + annualBalance +
'}';
}
}
并且,在 addStudent 表单中,添加两行来获取 新增的字段,如下图所示:

测试结果如下 GIF所示:

Δ总结
- 🆗,以上就是本篇博文的全部内容了,感谢阅读!
- 本篇我们通过一个简单的 表单提交数据 案例,验证了 SpringMVC 中的基本类型字符串的自动转换 和 特殊类型字符串的自动转换,其中基本类型的自动转换是 底层自动支持的,只要我们开启了 SpringMVC 注解驱动支持;而特殊类型字符串的自动转换需要用到特殊的注解如 @DateTimeFormat和 @NumberFormat。
- 大家也不要眼高手低,可以自己动手跟着实现一遍。我们下期再见!