Spring处理字段格式化的实战指南
目录
- 一、整体架构:Spring 类型转换体系
- 二、核心概念:Formatter SPI
- 1. Formatter<T> 接口
- (1) Printer<T>:对象 → 字符串(用于显示)
- (2) Parser<T>:字符串 → 对象(用于解析请求)
- 2. 内置 Formatter 实现(开箱即用)
- 三、高级特性:注解驱动的格式化(Annotation-driven Formatting)
- 1. AnnotationFormatterFactory<A extends Annotation>
- 示例:NumberFormatAnnotationFormatterFactory
- 2. 常见格式化注解(在 org.springframework.format.annotation 包下)
- 示例:
- 四、注册机制 SPI:如何让 Spring 知道你的 Formatter?
- 1. FormatterRegistry(核心注册中心)
- 2. FormattingConversionService(推荐实现类)
- 3. FormatterRegistrar(批量注册工具)
- 常见用途:
- 五、实战应用:如何配置全局日期格式?
- 目标:设置全局日期格式为 yyyyMMdd
- 方法一:Java 配置(推荐)
- 方法二:XML 配置(传统项目)
- 六、总结:一句话理解全文
- 七、常见问题解答(FAQ)
- Q1: Formatter 和 PropertyEditor 有什么区别?
- Q2: 在 Spring MVC 中怎么启用这些 formatter?
- Q3: jsR-310 时间类(LocalDate、LocalDateTime)支持吗?
一、整体架构:Spring 类型转换体系
Spring 提供了两套互补的类型转换机制:
| 模块 | 用途 | 特点 |
|---|---|---|
core.convert 包下的 Converter SPI | 通用类型转换(如 Long ↔ Date) | 通用性强,不涉及字符串格式化或 Locale |
format 包下的 FormattergNEbcXk SPI | 客户端字段格式化(如 String ↔ Date,带格式和语言) | 面向 UI 层,支持 Locale,适合 Web 应用 |
它们都由一个统一的服务接口管理:
ConversionService —— 是 Spring 内部进行类型转换的核心接口。
关键点:
- Converter:用于任意类型之间转换(后台逻辑用)。
- Formatter:专门用于 String 和目标类型之间转换(前端展示/提交用)。
- 两者都被 ConversionService 管理,可共存。
二、核心概念:Formatter SPI
1. Formatter<T> 接口
这是 Spring 为“客户端字段格式化”设计的核心接口:
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
它由两个子接口组成:
(1) Printer<T>:对象 → 字符串(用于显示)
String print(T fieldValue, Locale locale);
- 把 Java 对象(如
Date)按指定语言环境(Locale)格式化成字符串,用于页面展示。 - 例如:
new Date()→"2025-04-05"
(2) Parser<T>:字符串 → 对象(用于解析请求)
T parse(String clientValue, Locale locale) throws ParseException;
- 把用户提交的字符串(如表单输入)解析为 Java 对象。
- 例如:
"2025-04-05"→java.util.Date
要求:
- 实现必须是 线程安全 的(因为会被多个请求共享)。
- 解析失败应抛出 ParseException 或 IllegalArgumentException。
2. 内置 Formatter 实现(开箱即用)
Spring 提供了一些常用的 Formatter 实现:
| Formatter | 作用 |
|---|---|
NumberStyleFormatter | 格式化数字(支持千分位、小数点javascript等) |
CurrencyStyleFormatter | 显示货币(如 ¥1,234.00) |
PercentStyleFormatter | 百分比显示(如 50%) |
DateFormatter | 用 SimpleDateFormat 格式化日期 |
示例:DateFormatter 实现了 print() 和 parse(),使用 pattern 和 Locale 来格式化日期。
三、高级特性:注解驱动的格式化(Annotation-driven Formatting)
为了更方便地配置格式化规则,Spring 支持通过 注解 + 工厂模式 来绑定格式化逻辑。
1. AnnotationFormatterFactory<A extends Annotation>
这个接口的作用是:将某个注解映射到具体的 Printer 和 Parser
public interface AnnotationFormatterFactory<A extends Annotation> {
Set<Class<?>> getFieldTypes(); // 支持哪些字段类型?
Printer<?> getPrinter(A annotation, Class<?> fieldType);
Parser<?> getParser(A annotation, Class<?> fieldType);
}
示例:NumberFormatAnnotationFormatterFactory
它处理 @NumberFormat 注解:
@NumberFormat(style = Style.CURRENCY) private BigDecimal price;
- 当 Spring 遇到这个字段时,会查找注册的
AnnotationFormatterFactory - 找到后调用
getPrinter()/getParser()获取对应的格式化工厂 - 最终使用
CurrencyStyleFormatter来格式化金额
2. 常见格式化注解(在 org.springframework.format.annotation 包下)
| 注解 | 用途 |
|---|---|
@NumberFormat | 数字格式化(支持 pattern 或 style) |
@DateTimeFormat | 日期时间格式化(支持 ISO 格式、自定义 pattern) |
示例:
public class MyModel {
@DateTimeFormat(iso = ISO.DATE) // 格式:yyyy-MM-dd
private Date birthDate;
@NumberFormat(pattern = "#,###.00")
private Double salary;
}
这样就不需要在每个 controller 里手动写 new SimpleDateFormat(...),而是统一管理。
四、注册机制 SPI:如何让 Spring 知道你的 Formatter?
Spring 提供了几个 SPI 接口来注册格式化器。
1. FormatterRegistry(核心注册中心)
它是 ConverterRegistry 的扩展,允许你注册:
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
所有 formatter/converter 都要注册到这里才能生效。
2. FormattingConversionService(推荐实现类)
这是一个标准实现,实现了 ConversionService + FormatterRegistry,适合大多数场景。
你可以:
- 编程式注册(Java Config)
- 声明式注册(XML)
3. FormatterRegistrar(批量注册工具)
有时候你需要一次性注册多个 formatter(比如所有日期相关的),但直接注册不方便(比如类型擦除问题),这时可以用:
public interface FormatterRegistrar {
void registerFormatters(FormatterRegistry registry);
}
常见用途:
- 注册 Joda-Time 所有格式化器
- 设置全局日期格式
- 批量注册 JSR-310 (
java.time) 类型的 formatter
五、实战应用:如何配置全局日期格式?
默认情况下,未标注 @DateTimeFormat 的 Date 字段会用 DateFormat.SHORT(如 4/5/25)格式化,这通常不符合中国习惯。
目标:设置全局日期格式为 yyyyMMdd
方法一:Java 配置(推荐)
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// 创建 ConversionService,但不注册默认 formatter
DefaultFormattingConversionService service = new DefaultFormattingConversionService(false);
// 保留 @NumberFormat 支持
service.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// 设置全局日期格式:yyyyMMdd
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(service);
return service;
}
}
方法二:XML 配置(传统项目)
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="registerDefaultFormatters" value="false"/>
<property name="formatters">
<set>
<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory"/>
</set>
</property>
www.devze.com <property name="formatterRegistrars">
<set>
<bean class="org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar">
<property name="dateFormatter">
<bean class="org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean">
<property name="pattern" value="yyyyMMdd"/>
</bean>
</property>
</bean>
</set>
</property>
</bean>
六、总结:一句话理解全文
Spring 的 Formatter SPI 是为 客户端环境(如 Web 页面)设计的一套 基于 Locale 的字符串 ↔ 对象 转换机制,相比通用的 Converter,它更适合处理用户输入和输出的格式化需求,且支持注解驱动(如 @DateTimeFormat)、可集中配置、易于扩展。
七、常见问题解答(FAQ)
Q1: Formatter 和 PropertyEditor 有什么区别?
PropertyEditor是 JavaBeans 规范的老技术,线程不安全,API 不够灵活。Formatter是 Spring 3 新引入的,线程安全、强类型、支持 Locale,推荐替代PropertyEditor。
Q2: 在 Spring MVC 中怎么启用这些 formatter?
只要把自定义的 FormattingConversionService 注入到 Spring MVC 的配置中即可:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
gNEbcXk registry.addFormatter(new DateFormatter("yyyyMMdd"));
}
}
或者使用 <mvc:annotation-driven conversion-service="conversionService"/> XML 配置。
Q3: JSR-310 时间类(LocalDatphpe、LocalDateTime)支持吗?
支持!Spring 会自动注册 java.time 类型的 formatter。你可以通过 DateTimeFormatterRegistrar 控制其格式。
如果你正在开发 Web 应用,尤其是需要处理表单提交、日期显示、货币格式等功能,那么这一套 Formatter 机制就是你应该掌握的核心技能之一。
需要我根据你的具体场景(比如 Spring Boot 项目)给出一个完整的配置示例吗?
以上就是Spring处理字段格式化的实战指南的详细内容,更多关于Spring处理字段格式化的资料请关注编程客栈(www.devze.com)其它相关文章!
加载中,请稍侯......
精彩评论