Skip to content

SpringMVC教程 - 12 类型转换

在上一章的消息转换器章节,是将请求体响应体与Java对象的转换,一般涉及到使用 @RequestBody@ResponseBodyRequestEntityResponseEntity

而对于 URL 中的参数、表单提交的数据(x-www-form-urlencoded),则是使用类型转换,将请求的字符串转换为 Java 的各种类型,比如 intlongDate,甚至是自定义对象。

大概的处理流程如下:

简单的说,也就是 SpringMVC 会使用 ConversionService 调用合适的转换器 Formatter 或 Converter 将 String 转换为对应的类型。

当然上面的转换都是 SpringMVC 帮我们完成了,我们只需要了解使用方法就可以了。

12.1 默认的类型转换器

SpringMVC已经内置了许多常用的类型转换器,可以自动处理基本的数据类型转换。例如:

  • 字符串转基本类型String -> int, long, double, boolean
  • 字符串转日期String -> Date, LocalDate, LocalDateTime
  • 字符串转集合String -> List, Set, Map

我们前面请求接口的时候,传递的数据都是字符串的,只是 SpringMVC 帮我们转换为 Controller 参数对应的类型。

下面通过一些例子看一下SpringMVC的默认类型转换功能。

1 基本类型转换

java
package com.foooor.hellospringmvc.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@Controller
public class TypeConversionController {

    @GetMapping("/convert/basic")
    @ResponseBody
    public String basicConversion(
            @RequestParam int id,
            @RequestParam String name,
            @RequestParam double salary,
            @RequestParam boolean active) {

        log.info("请求参数: id={}, name={}, salary={}, active={}", id, name, salary, active);

        return String.format("ID: %d, Name: %s, Salary: %.2f, Active: %s",
                id, name, salary, active);
    }
}
  • 当我们访问 http://localhost:8080/convert/basic?id=1001&name=张三&salary=5000.50&active=true 时,SpringMVC 会自动将URL参数转换为对应的类型。
  • 在上面的转换中,SpringMVC 会使用各种 Converter,例如 StringToIntegerConverter ,将字符串转换为整形、浮点型、布尔型。

2 集合类型转换

SpringMVC 可以自动将逗号分隔的字符串转换为集合类型。

java
@GetMapping("/convert/collection")
@ResponseBody
public String collectionConversion(
        @RequestParam List<String> names,
        @RequestParam Set<Integer> ids) {

    return String.format("Names: %s, IDs: %s", names, ids);
}
  • 当我们访问http://localhost:8080/convert/collection?names=张三,李四,王五&ids=1001,1002,1003时,SpringMVC会自动将逗号分隔的字符串转换为对应的集合。
  • 在上面的转换中,SpringMVC 会使用 CollectionConverter 将字符串转换为字符串数组,然后对每个元素调用元素类型的转换器。

3 日期类型转换

SpringMVC 默认也正常将字符串转换为 DateLocalDateTimeLocalDateTime 类型。

举个栗子:

java
@GetMapping("/convert/date")
@ResponseBody
public String dateConversion(
        @RequestParam Date date,
        @RequestParam LocalDate localDate,
        @RequestParam LocalDateTime localDateTime) {

    log.info("请求参数: date={}, localDate={}, localDateTime={}", date, localDate, localDateTime);

    return String.format("Date: %s, LocalDate: %s, LocalDateTime: %s",
            date, localDate, localDateTime);
}
  • 但是这里非常操蛋,请求时候的字符串格式要求很严格,一不对就会报错,上面的接口可以使用 http://localhost:8080/convert/date?date=2025/12/20 10:30:45&localDate=2025-12-20&localDateTime=2025-12-20T10:30:45 来访问。date 的日期部分写成 2025-12-20 就很可能报错。

所以我们可以在方法参数上添加 @DateTimeFormat 注解,指定日志字符串的格式:

java
@GetMapping("/convert/date")
@ResponseBody
public String dateConversion(
        @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date,
        @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate localDate,
        @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime localDateTime) {

    log.info("请求参数: date={}, localDate={}, localDateTime={}", date, localDate, localDateTime);

    return String.format("Date: %s, LocalDate: %s, LocalDateTime: %s",
            date, localDate, localDateTime);
}
  • 在参数上通过 @DateTimeFormat 指定时间参数字符串的格式,按照这个格式传递就可以了,http://localhost:8080/convert/date?date=2025-12-20 10:30:45&localDate=2025-12-20&localDateTime=2025-12-20 10:30:45
  • 需要注意,Date 类型可以同时指定日期和时间,或者其中一部分;LocalDate 只能指定日期,指定时间也不生效;LocalDateTime 需要同时指定日期和时间,并同时传递日期和时间数据,否则会报错。
  • 在上面的转换中,SpringMVC 会使用 DateFormatter 将字符串转换为 Date,使用 DateTimeFormatter 将字符串转换为 LocalDate 或 LocalDateTime。

4 路径变量转换

SpringMVC 的 @PathVariable 可以把 URL 路径中的参数直接绑定到方法参数中,并支持类型转换。

java
@GetMapping("/convert/path/{id}/{name}/{salary}/{date}")
@ResponseBody
public String pathVariableConversion(
        @PathVariable int id,
        @PathVariable String name,
        @PathVariable double salary,
        @PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {

    log.info("请求参数: id={}, name={}, salary={}, date={}", id, name, salary, date);
    return String.format("ID: %d, Name: %s, Salary: %.2f, Date: %s", id, name, salary, date);
}
  • 通过 http://localhost:8080/convert/path/1001/张三/5000.50/2025-12-20 访问,就可以拿到参数了。
  • 具体转换方式和上面转换方式是一样的,使用 Converter 或 Formatter。

5 对象类型转换

SpringMVC 可以将请求参数自动绑定到对象上,它会根据对象的属性名匹配请求参数的name。

举个栗子:

我们首先定义一个类,BookDto.java,如下

java
package com.foooor.hellospringmvc.dto;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;

@Data
public class BookDto {
    private int id;
    private String name;
    private double salary;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")  // 定义日期时间格式
    private LocalDateTime date;
}
  • 类中定义了不同类型的属性;使用 @DateTimeFormat 注解定义日期的格式。

然后定义 Controller 中的方法接收请求的参数:

java
@PostMapping("/convert/object")
@ResponseBody
public String objectConversion(BookDto book) {
    log.info("请求参数: {}", book);
    return book.toString();
}
  • 通过 POST 方法,使用 URL http://localhost:8080/convert/object?id=1001&name=张三&salary=5000.50&active=true&date=2025-12-20 10:30:45 就可以请求接口,Controller 中就可以获取到参数了。
  • 也可以使用表单的方式提交,但需要注意,表单的请求方法需要使用 POST 方式提交,如果使用 GET 方式,可能导致服务器不解析请求体,导致获取不到参数。
  • 在上面的转换方式中,首先创建对象,然后通过 ConversionService 会调用合适的转换器 Formatter 或 Converter 将 String 转换为对应的类型。
  • 需要注意,这里不是 JSON 格式的 body!这里没有使用 @RequestBody 注解,将 JSON 格式的请求体转换为对象,是消息转换器 MappingJackson2HttpMessageConverter 负责的,对于JSON转对象,使用@DateTimeFormat 是不生效的,需要在属性上添加 @JsonFormat(pattern = "yyyy-MM-dd") 告诉 Jackson 字符串的格式。

12.2 自定义Converter

虽然 SpringMVC 提供了很多默认的类型转换器,但在某些情况下,可能并不能满足我们的要求,我们就需要自定义类型转换逻辑。

但是有一个问题,什么时候使用的是 Formatter,什么时候使用的是 Converter 呢?

如果你的类型转换 和格式无关,只涉及类型 → 类型,那就用 Converter

典型例子:

  • String → Integer
  • String → Boolean
  • Integer → Long
  • String → Double
  • String → 自定义对象
  • 等等

一句话:Converter 用于普通类型转换,不关心格式。

如果需要“格式模板”来解析字符串,例如时间、金额、数字带格式的情况,就应该用 Formatter

典型例子:

  • "2025-01-01" → LocalDate
  • "2025-01-01 10:00:00" → LocalDateTime
  • "1,234.56" → BigDecimal
  • "¥9.9" → Money
  • 日期时间转换(配合 @DateTimeFormat)

一句话:Formatter 用于带格式的 String ↔ 对象转换。

但其实使用 Formatter 能实现的,使用 Converter 也可以实现。


假设现在有一个需求,通过传递参数 ?user=123,ZhangSan,zhangsan@foooor.com ,最终转换为 Controller 方法的 User 类型的参数。

1 实现Converter接口

首先得有 User 类,之前有过:

java
package com.foooor.hellospringmvc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private String email;
}

现在,我们想将格式为id,name,email的字符串转换为User对象。我们可以创建一个自定义的转换器,可以通过实现 Converter<S, T> 接口来创建:

java
package com.foooor.hellospringmvc.converter;
import com.foooor.hellospringmvc.pojo.User;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class StringToUserConverter implements Converter<String, User> {

    /**
     * 转换字符串到 User 对象
     * 假设字符串格式为 "id,name,email"
     */
    @Override
    public User convert(String source) {
        try {
            // 假设字符串格式为 "id,name,email"
            String[] parts = source.split(",");
            if (parts.length == 3) {
                Long id = Long.parseLong(parts[0]);
                String name = parts[1];
                String email = parts[2];
                return new User(id, name, email);
            }
        } catch (Exception e) {
             // 处理转换异常
             e.printStackTrace();
        }
        return null;
    }
}
  • 实现 Converter 接口,重写 convert 方法,同时添加 @Component 交给 Spring 容器来管理。

2 注册类型转换器

在 SpringMVC 配置文件中添加如下配置:

xml
<!-- 启用 Spring MVC 注解支持 -->
<mvc:annotation-driven conversion-service="conversionService">
    <!-- 其他配置,略... -->
</mvc:annotation-driven>

<!-- 定义 ConversionService -->
<bean id="conversionService"
      class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="stringToUserConverter"/>
        </set>
    </property>
</bean>
  • <mvc:annotation-driven> 标签上添加 conversion-service="conversionService" ,然后在 conversionService 中注册自定义的 stringToUserConverter

3 测试

通过使用 URL:http://localhost:8080/convert/user?user=123,ZhangSan,zhangsan@foooor.com 来请求,就可以将参数赋值给 User 对象中的参数了。

java
@GetMapping("/convert/user")
@ResponseBody
public String userConversion(User user) {
   log.info("请求参数: {}", user);
   return user.toString();
}

12.3 类型转换错误处理

当类型转换失败时,SpringMVC会抛出异常,现在我们没有捕获这个异常,所以出现错误的时候,页面会出现如下很丑的页面:

所以我们需要捕获和处理这些异常,返回统一的错误页面或 JSON 数据,这个在后面统一异常处理的章节再讲。

内容未完......