Spring處理字段格式化的實戰(zhàn)指南
一、整體架構(gòu):Spring 類型轉(zhuǎn)換體系
Spring 提供了兩套互補(bǔ)的類型轉(zhuǎn)換機(jī)制:
| 模塊 | 用途 | 特點 |
|---|---|---|
core.convert 包下的 Converter SPI | 通用類型轉(zhuǎn)換(如 Long ↔ Date) | 通用性強(qiáng),不涉及字符串格式化或 Locale |
format 包下的 Formatter SPI | 客戶端字段格式化(如 String ↔ Date,帶格式和語言) | 面向 UI 層,支持 Locale,適合 Web 應(yīng)用 |
它們都由一個統(tǒng)一的服務(wù)接口管理:ConversionService —— 是 Spring 內(nèi)部進(jìn)行類型轉(zhuǎn)換的核心接口。
關(guān)鍵點:
- Converter:用于任意類型之間轉(zhuǎn)換(后臺邏輯用)。
- Formatter:專門用于 String 和目標(biāo)類型之間轉(zhuǎn)換(前端展示/提交用)。
- 兩者都被 ConversionService 管理,可共存。
二、核心概念:Formatter SPI
1. Formatter<T> 接口
這是 Spring 為“客戶端字段格式化”設(shè)計的核心接口:
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
它由兩個子接口組成:
(1) Printer<T>:對象 → 字符串(用于顯示)
String print(T fieldValue, Locale locale);
- 把 Java 對象(如
Date)按指定語言環(huán)境(Locale)格式化成字符串,用于頁面展示。 - 例如:
new Date()→"2025-04-05"
(2) Parser<T>:字符串 → 對象(用于解析請求)
T parse(String clientValue, Locale locale) throws ParseException;
- 把用戶提交的字符串(如表單輸入)解析為 Java 對象。
- 例如:
"2025-04-05"→java.util.Date
要求:
- 實現(xiàn)必須是 線程安全 的(因為會被多個請求共享)。
- 解析失敗應(yīng)拋出 ParseException 或 IllegalArgumentException。
2. 內(nèi)置 Formatter 實現(xiàn)(開箱即用)
Spring 提供了一些常用的 Formatter 實現(xiàn):
| Formatter | 作用 |
|---|---|
NumberStyleFormatter | 格式化數(shù)字(支持千分位、小數(shù)點等) |
CurrencyStyleFormatter | 顯示貨幣(如 ¥1,234.00) |
PercentStyleFormatter | 百分比顯示(如 50%) |
DateFormatter | 用 SimpleDateFormat 格式化日期 |
示例:DateFormatter 實現(xiàn)了 print() 和 parse(),使用 pattern 和 Locale 來格式化日期。
三、高級特性:注解驅(qū)動的格式化(Annotation-driven Formatting)
為了更方便地配置格式化規(guī)則,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;
- 當(dāng) Spring 遇到這個字段時,會查找注冊的
AnnotationFormatterFactory - 找到后調(diào)用
getPrinter()/getParser()獲取對應(yīng)的格式化工廠 - 最終使用
CurrencyStyleFormatter來格式化金額
2. 常見格式化注解(在 org.springframework.format.annotation 包下)
| 注解 | 用途 |
|---|---|
@NumberFormat | 數(shù)字格式化(支持 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(...),而是統(tǒng)一管理。
四、注冊機(jī)制 SPI:如何讓 Spring 知道你的 Formatter?
Spring 提供了幾個 SPI 接口來注冊格式化器。
1. FormatterRegistry(核心注冊中心)
它是 ConverterRegistry 的擴(kuò)展,允許你注冊:
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
所有 formatter/converter 都要注冊到這里才能生效。
2. FormattingConversionService(推薦實現(xiàn)類)
這是一個標(biāo)準(zhǔn)實現(xiàn),實現(xiàn)了 ConversionService + FormatterRegistry,適合大多數(shù)場景。
你可以:
- 編程式注冊(Java Config)
- 聲明式注冊(XML)
3. FormatterRegistrar(批量注冊工具)
有時候你需要一次性注冊多個 formatter(比如所有日期相關(guān)的),但直接注冊不方便(比如類型擦除問題),這時可以用:
public interface FormatterRegistrar {
void registerFormatters(FormatterRegistry registry);
}
常見用途:
- 注冊 Joda-Time 所有格式化器
- 設(shè)置全局日期格式
- 批量注冊 JSR-310 (
java.time) 類型的 formatter
五、實戰(zhàn)應(yīng)用:如何配置全局日期格式?
默認(rèn)情況下,未標(biāo)注 @DateTimeFormat 的 Date 字段會用 DateFormat.SHORT(如 4/5/25)格式化,這通常不符合中國習(xí)慣。
目標(biāo):設(shè)置全局日期格式為 yyyyMMdd
方法一:Java 配置(推薦)
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// 創(chuàng)建 ConversionService,但不注冊默認(rèn) formatter
DefaultFormattingConversionService service = new DefaultFormattingConversionService(false);
// 保留 @NumberFormat 支持
service.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// 設(shè)置全局日期格式:yyyyMMdd
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(service);
return service;
}
}
方法二:XML 配置(傳統(tǒng)項目)
<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>
<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>
六、總結(jié):一句話理解全文
Spring 的 Formatter SPI 是為 客戶端環(huán)境(如 Web 頁面)設(shè)計的一套 基于 Locale 的字符串 ↔ 對象 轉(zhuǎn)換機(jī)制,相比通用的 Converter,它更適合處理用戶輸入和輸出的格式化需求,且支持注解驅(qū)動(如 @DateTimeFormat)、可集中配置、易于擴(kuò)展。
七、常見問題解答(FAQ)
Q1: Formatter 和 PropertyEditor 有什么區(qū)別?
PropertyEditor是 JavaBeans 規(guī)范的老技術(shù),線程不安全,API 不夠靈活。Formatter是 Spring 3 新引入的,線程安全、強(qiáng)類型、支持 Locale,推薦替代PropertyEditor。
Q2: 在 Spring MVC 中怎么啟用這些 formatter?
只要把自定義的 FormattingConversionService 注入到 Spring MVC 的配置中即可:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new DateFormatter("yyyyMMdd"));
}
}
或者使用 <mvc:annotation-driven conversion-service="conversionService"/> XML 配置。
Q3: JSR-310 時間類(LocalDate、LocalDateTime)支持嗎?
支持!Spring 會自動注冊 java.time 類型的 formatter。你可以通過 DateTimeFormatterRegistrar 控制其格式。
如果你正在開發(fā) Web 應(yīng)用,尤其是需要處理表單提交、日期顯示、貨幣格式等功能,那么這一套 Formatter 機(jī)制就是你應(yīng)該掌握的核心技能之一。
需要我根據(jù)你的具體場景(比如 Spring Boot 項目)給出一個完整的配置示例嗎?
以上就是Spring處理字段格式化的實戰(zhàn)指南的詳細(xì)內(nèi)容,更多關(guān)于Spring處理字段格式化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaBean valication驗證實現(xiàn)方法示例
這篇文章主要介紹了JavaBean valication驗證實現(xiàn)方法,結(jié)合實例形式分析了JavaBean valication驗證相關(guān)概念、原理、用法及操作注意事項,需要的朋友可以參考下2020-03-03
Spring Data Jpa實現(xiàn)自定義repository轉(zhuǎn)DTO
這篇文章主要介紹了Spring Data Jpa實現(xiàn)自定義repository轉(zhuǎn)DTO,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
SpringBoot+Dubbo+Zookeeper知識整合過程詳解
本文首先介紹了分布式系統(tǒng)的基本概念和分類,包括單一應(yīng)用架構(gòu)、垂直應(yīng)用架構(gòu)、分布式服務(wù)架構(gòu)和流動計算架構(gòu),通過一個完整的Spring Boot + Dubbo + Zookeeper框架搭建示例,展示了如何將這些技術(shù)整合到一個實際的項目中,感興趣的朋友一起看看吧2025-02-02
Java中CountDownLatch工具類詳細(xì)解析
這篇文章主要介紹了Java中CountDownLatch工具類詳細(xì)解析,創(chuàng)建CountDownLatch對象時,會傳入一個count數(shù)值,該對象每次調(diào)用countDown()方法會使count?--?,就是count每次減1,需要的朋友可以參考下2023-11-11
Spring?Boot教程之提高開發(fā)效率必備工具lombok
這篇文章主要介紹了Spring?Boot教程之提高開發(fā)效率必備工具lombok的相關(guān)資料,需要的朋友可以參考下2022-08-08

