乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

类型转换与数据绑定 — Converter / DataBinder

一句话定位:Spring 的类型转换与数据绑定是 Web 请求的"翻译官"——把用户提交的字符串参数翻译成 Java 对象能理解的类型,把对象属性绑定到表单字段,让前后端数据流通无障碍。


为什么需要类型转换与数据绑定?

HTTP 请求中的一切都是字符串:URL 参数、表单字段、Header 值。但 Java 方法参数可能是 Long、LocalDate、BigDecimal、枚举类型,甚至是复杂对象。Spring 的 DataBinder(数据绑定器) 和 Converter(转换器) 就是负责这个"翻译"工作的。


Converter 体系

核心接口

Spring 提供了两套转换接口:

接口用途示例
Converter<S, T>单向转换StringToIntegerConverter
ConverterFactory<S, R>批量转换(一族类型)StringToNumberConverterFactory
GenericConverter复杂条件转换StringToEnumConverter
Formatter<T>格式化(考虑 Locale)DateFormatter、NumberFormatter

自定义 Converter

乐途公司的商品 ID 有特殊格式:FX-10086,但数据库里存的是纯数字 10086。小崔写了一个 Converter:

@Component
public class FeixiangProductIdConverter implements Converter<String, Long> {

    private static final String PREFIX = "FX-";

    @Override
    public Long convert(String source) {
        if (source == null || source.isBlank()) {
            return null;
        }
        if (source.startsWith(PREFIX)) {
            return Long.valueOf(source.substring(PREFIX.length()));
        }
        // 也支持纯数字
        return Long.valueOf(source);
    }
}

注册到 Spring:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new FeixiangProductIdConverter());
    }
}

Controller 中直接使用:

@GetMapping("/api/products/{productId}")
public Product getProduct(@PathVariable Long productId) {
    // 请求 /api/products/FX-10086 时,自动转换为 10086L
    return productService.findById(productId);
}

自定义 Formatter

乐途公司的价格显示需要保留两位小数,且支持千分位:

@Component
public class CurrencyFormatter implements Formatter<BigDecimal> {

    @Override
    public BigDecimal parse(String text, Locale locale) throws ParseException {
        // 解析:去掉 ¥ 和逗号
        String clean = text.replace("¥", "").replace(",", "").trim();
        return new BigDecimal(clean);
    }

    @Override
    public String print(BigDecimal object, Locale locale) {
        // 格式化:¥1,888.88
        return String.format(locale, "¥%,.2f", object);
    }
}

DataBinder 详解

基本绑定流程

DataBinder 是 Spring 数据绑定的核心类,负责:

  1. 将请求参数名映射到对象属性名
  2. 调用 Converter 做类型转换
  3. 调用 Validator 做数据校验
@RestController
public class OrderController {

    @PostMapping("/api/orders/form")
    public Order createOrderFromForm(@ModelAttribute OrderForm form) {
        // Spring 自动将请求参数绑定到 OrderForm 对象
        return orderService.create(form);
    }
}

自定义属性编辑器(PropertyEditor)

@InitBinder
public void initBinder(WebDataBinder binder) {
    // 注册自定义编辑器
    binder.registerCustomEditor(Date.class, new CustomDateEditor(
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true
    ));

    // 指定允许绑定的字段(防止恶意字段注入)
    binder.setAllowedFields("userId", "productId", "quantity", "remark");

    // 必填字段
    binder.setRequiredFields("userId", "productId", "quantity");
}

编程式使用 DataBinder

@Service
public class OrderImportService {

    public OrderForm bindFromMap(Map<String, String> params) {
        OrderForm form = new OrderForm();
        DataBinder binder = new DataBinder(form);
        binder.registerCustomEditor(Long.class, "productId", new FeixiangProductIdEditor());

        MutablePropertyValues pvs = new MutablePropertyValues(params);
        binder.bind(pvs);

        // 获取绑定结果
        BindingResult result = binder.getBindingResult();
        if (result.hasErrors()) {
            throw new IllegalArgumentException("参数绑定失败: " + result.getAllErrors());
        }
        return form;
    }
}

乐途场景:订单表单绑定

乐途公司的运营后台需要导入批量订单,数据来自 Excel 解析后的 Map 列表:

public class OrderForm {
    private Long userId;
    private Long productId;      // 支持 FX-10086 格式
    private Integer quantity;
    private LocalDate deliveryDate;
    private BigDecimal discount; // 支持 ¥10.00 格式
    private String remark;
    // getters / setters...
}

配置转换器:

@Configuration
public class FeixiangWebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // 商品 ID 转换
        registry.addConverter(new FeixiangProductIdConverter());

        // 日期转换
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        registry.addFormatterForFieldType(LocalDate.class,
            new TemporalAccessorFormatter(dateFormatter));

        // 货币转换
        registry.addFormatterForFieldType(BigDecimal.class, new CurrencyFormatter());
    }
}

Controller 绑定:

@PostMapping("/api/orders/batch")
public List<Order> createBatchOrders(@RequestBody List<Map<String, String>> rows) {
    return rows.stream()
        .map(orderImportService::bindFromMap)
        .map(orderService::create)
        .collect(Collectors.toList());
}

注意事项

注意点说明
类型转换失败默认会抛出 TypeMismatchException,可被 @ExceptionHandler 捕获返回 400
字段安全用 setAllowedFields() 白名单限制可绑定字段,防止恶意注入(如 isAdmin=true)
空值处理空字符串 "" 默认转换为 null,可用 binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)) 控制
集合绑定支持 List、Set、Map 绑定,参数名格式:items[0].name、params['key']
嵌套对象支持多级嵌套绑定,如 address.city、address.zipCode

常见面试题

Q1:Converter 和 Formatter 有什么区别?

Converter 是通用类型转换(S → T),不考虑本地化。Formatter 是特化的 Converter,专门处理 String ↔ T,且考虑 Locale(如日期、数字的本地化格式)。Spring MVC 的 Web 层优先使用 Formatter。

Q2:Spring 默认注册了哪些 Converter?

Spring 核心容器默认注册了 100+ 个转换器,覆盖:基本类型(String ↔ int/long/boolean)、集合类型、数组类型、枚举类型、Properties、Resource 等。Web 层额外注册了日期、数字的 Formatter。

Q3:@InitBinder 的作用范围是什么?

@InitBinder 标注的方法只对当前 Controller 生效。如果想全局生效,在 @ControllerAdvice 中定义 @InitBinder 方法。

Q4:如何防止数据绑定中的安全漏洞(如字段注入攻击)?

使用 DataBinder.setAllowedFields() 白名单限制可绑定字段,或 setDisallowedFields() 黑名单排除敏感字段(如 password、isAdmin、role)。Spring Boot 2.x+ 默认已加强此方面的防护。

上一页
校验 Validation — JSR-303 / JSR-380 Bean Validation