SpringMVC 数据格式化处理 详解

目录

一、拾枝杂谈

0.为什么需要数据格式化?

1.什么是数据格式化?

2.如何实现数据转换?

3.基本类型的自动转换:

4.特殊类型的自动转换:

二、应用实例

1.需求:

2.环境搭建:

3.基本格式类型字符串自动转换测试:

4.特殊格式类型和字符串自动转换测试:

Δ总结


一、拾枝杂谈

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
  • 大家也不要眼高手低,可以自己动手跟着实现一遍。我们下期再见!
相关推荐
测试员周周1 小时前
【Appium 系列】第08节-pytest 集成 — conftest.py 中的 fixture 与 hook
开发语言·人工智能·python·功能测试·appium·测试用例·pytest
SunnyDays10111 小时前
Java 实现 PDF 中文文本查找与高亮的四种方法
java·pdf·查找文字
倒流时光三十年1 小时前
PostgreSQL 一次由 string_agg 引发的数据错位 Bug 深度复盘
java·postgresql·string_agg
Hui_AI7201 小时前
电商桌面自动化实战:用RPA实现抖店批量铺货
运维·开发语言·人工智能·自然语言处理·自动化·开源软件·rpa
人道领域1 小时前
【LeetCode刷题日记】递归与回溯实战 257.二叉树的所有路径——一篇文章彻底搞懂回溯
开发语言·python·算法·leetcode
Gofarlic_OMS1 小时前
Mastercam浮动许可利用率低:软件许可浪费,回收再分配
java·大数据·开发语言·架构·制造
AC赳赳老秦1 小时前
OpenClaw与飞书多维表格联动:自动同步工作数据、生成统计图表,实现高效管理
java·数据库·python·信息可视化·飞书·deepseek·openclaw
吃好睡好便好1 小时前
在Matlab中用sphere( )函数绘制球面图
开发语言·前端·javascript·学习·算法·matlab·信息可视化
lynnlovemin1 小时前
二分查找与二分答案算法详解(基于C++实现)
c语言·开发语言·算法·二分查找·二分答案