Spring處理字段格式化的實戰(zhàn)指南
一、整體架構(gòu):Spring 類型轉(zhuǎn)換體系
Spring 提供了兩套互補的類型轉(zhuǎn)換機制:
| 模塊 | 用途 | 特點 |
|---|---|---|
core.convert 包下的 Converter SPI | 通用類型轉(zhuǎn)換(如 Long ↔ Date) | 通用性強,不涉及字符串格式化或 Locale |
format 包下的 Formatter SPI | 客戶端字段格式化(如 String ↔ Date,帶格式和語言) | 面向 UI 層,支持 Locale,適合 Web 應(yīng)用 |
它們都由一個統(tǒng)一的服務(wù)接口管理:ConversionService —— 是 Spring 內(nèi)部進行類型轉(zhuǎn)換的核心接口。
關(guān)鍵點:
- Converter:用于任意類型之間轉(zhuǎn)換(后臺邏輯用)。
- Formatter:專門用于 String 和目標類型之間轉(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;
- 當 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)一管理。
四、注冊機制 SPI:如何讓 Spring 知道你的 Formatter?
Spring 提供了幾個 SPI 接口來注冊格式化器。
1. FormatterRegistry(核心注冊中心)
它是 ConverterRegistry 的擴展,允許你注冊:
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
所有 formatter/converter 都要注冊到這里才能生效。
2. FormattingConversionService(推薦實現(xià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)用:如何配置全局日期格式?
默認情況下,未標注 @DateTimeFormat 的 Date 字段會用 DateFormat.SHORT(如 4/5/25)格式化,這通常不符合中國習慣。
目標:設(shè)置全局日期格式為 yyyyMMdd
方法一:Java 配置(推薦)
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// 創(chuàng)建 ConversionService,但不注冊默認 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)換機制,相比通用的 Converter,它更適合處理用戶輸入和輸出的格式化需求,且支持注解驅(qū)動(如 @DateTimeFormat)、可集中配置、易于擴展。
七、常見問題解答(FAQ)
Q1: Formatter 和 PropertyEditor 有什么區(qū)別?
PropertyEditor是 JavaBeans 規(guī)范的老技術(shù),線程不安全,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) {
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 機制就是你應(yīng)該掌握的核心技能之一。
需要我根據(jù)你的具體場景(比如 Spring Boot 項目)給出一個完整的配置示例嗎?
以上就是Spring處理字段格式化的實戰(zhàn)指南的詳細內(nèi)容,更多關(guān)于Spring處理字段格式化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java?Spring?AOP源碼解析之事務(wù)實現(xiàn)原理
這篇文章主要為大家介紹了Java?Spring?AOP事務(wù)實現(xiàn)原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
MyBatis在Mapper中傳遞多個參數(shù)的四種方法詳解
這篇文章主要介紹了MyBatis在Mapper中傳遞多個參數(shù)的四種方法,文章通過代碼示例介紹的非常詳細,具有一定的參考價值,,需要的朋友可以參考下2023-09-09
使用Java和WebSocket實現(xiàn)網(wǎng)頁聊天室實例代碼
WebSocket是HTML5一種新的協(xié)議,它實現(xiàn)了瀏覽器與服務(wù)器全雙工通信,這里就將使用WebSocket來開發(fā)網(wǎng)頁聊天室,對Java和WebSocket實現(xiàn)網(wǎng)頁聊天室的實例代碼感興趣的朋友一起學習吧2016-06-06
java讀取枚舉類的值轉(zhuǎn)成list和map方式
這篇文章主要介紹了java讀取枚舉類的值轉(zhuǎn)成list和map方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
Spring Validation數(shù)據(jù)校驗詳解
SpringValidation是Spring框架的數(shù)據(jù)校驗抽象層,支持注解、分組、嵌套校驗及國際化,默認集成HibernateValidator,可自定義校驗器滿足復(fù)雜需求,適用于Web參數(shù)校驗和業(yè)務(wù)邏輯驗證2025-09-09
淺談SpringBoot中的@Conditional注解的使用
這篇文章主要介紹了淺談SpringBoot中的@Conditional注解的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04
java.sql.SQLException問題解決以及注意事項
這篇文章主要給大家介紹了關(guān)于java.sql.SQLException問題解決以及注意事項的相關(guān)資料,這個問題其實很好解決,文中通過圖文將解決的辦法介紹的很詳細,需要的朋友可以參考下2023-07-07
Java畢業(yè)設(shè)計實戰(zhàn)之食品溯源系統(tǒng)的實現(xiàn)
這是一個使用了java+Springboot+Maven+mybatis+Vue+mysql+wd開發(fā)的食品溯源系統(tǒng),是一個畢業(yè)設(shè)計的實戰(zhàn)練習,具有食品溯源該有的所有功能,感興趣的朋友快來看看吧2022-01-01

