Spring處理字段格式化的實(shí)戰(zhàn)指南
一、整體架構(gòu):Spring 類(lèi)型轉(zhuǎn)換體系
Spring 提供了兩套互補(bǔ)的類(lèi)型轉(zhuǎn)換機(jī)制:
| 模塊 | 用途 | 特點(diǎn) |
|---|---|---|
core.convert 包下的 Converter SPI | 通用類(lèi)型轉(zhuǎn)換(如 Long ↔ Date) | 通用性強(qiáng),不涉及字符串格式化或 Locale |
format 包下的 Formatter SPI | 客戶(hù)端字段格式化(如 String ↔ Date,帶格式和語(yǔ)言) | 面向 UI 層,支持 Locale,適合 Web 應(yīng)用 |
它們都由一個(gè)統(tǒng)一的服務(wù)接口管理:ConversionService —— 是 Spring 內(nèi)部進(jìn)行類(lèi)型轉(zhuǎn)換的核心接口。
關(guān)鍵點(diǎn):
- Converter:用于任意類(lèi)型之間轉(zhuǎn)換(后臺(tái)邏輯用)。
- Formatter:專(zhuān)門(mén)用于 String 和目標(biāo)類(lèi)型之間轉(zhuǎn)換(前端展示/提交用)。
- 兩者都被 ConversionService 管理,可共存。
二、核心概念:Formatter SPI
1. Formatter<T> 接口
這是 Spring 為“客戶(hù)端字段格式化”設(shè)計(jì)的核心接口:
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
它由兩個(gè)子接口組成:
(1) Printer<T>:對(duì)象 → 字符串(用于顯示)
String print(T fieldValue, Locale locale);
- 把 Java 對(duì)象(如
Date)按指定語(yǔ)言環(huán)境(Locale)格式化成字符串,用于頁(yè)面展示。 - 例如:
new Date()→"2025-04-05"
(2) Parser<T>:字符串 → 對(duì)象(用于解析請(qǐng)求)
T parse(String clientValue, Locale locale) throws ParseException;
- 把用戶(hù)提交的字符串(如表單輸入)解析為 Java 對(duì)象。
- 例如:
"2025-04-05"→java.util.Date
要求:
- 實(shí)現(xiàn)必須是 線(xiàn)程安全 的(因?yàn)闀?huì)被多個(gè)請(qǐng)求共享)。
- 解析失敗應(yīng)拋出 ParseException 或 IllegalArgumentException。
2. 內(nèi)置 Formatter 實(shí)現(xiàn)(開(kāi)箱即用)
Spring 提供了一些常用的 Formatter 實(shí)現(xiàn):
| Formatter | 作用 |
|---|---|
NumberStyleFormatter | 格式化數(shù)字(支持千分位、小數(shù)點(diǎn)等) |
CurrencyStyleFormatter | 顯示貨幣(如 ¥1,234.00) |
PercentStyleFormatter | 百分比顯示(如 50%) |
DateFormatter | 用 SimpleDateFormat 格式化日期 |
示例:DateFormatter 實(shí)現(xiàn)了 print() 和 parse(),使用 pattern 和 Locale 來(lái)格式化日期。
三、高級(jí)特性:注解驅(qū)動(dòng)的格式化(Annotation-driven Formatting)
為了更方便地配置格式化規(guī)則,Spring 支持通過(guò) 注解 + 工廠(chǎng)模式 來(lái)綁定格式化邏輯。
1. AnnotationFormatterFactory<A extends Annotation>
這個(gè)接口的作用是:將某個(gè)注解映射到具體的 Printer 和 Parser
public interface AnnotationFormatterFactory<A extends Annotation> {
Set<Class<?>> getFieldTypes(); // 支持哪些字段類(lèi)型?
Printer<?> getPrinter(A annotation, Class<?> fieldType);
Parser<?> getParser(A annotation, Class<?> fieldType);
}
示例:NumberFormatAnnotationFormatterFactory
它處理 @NumberFormat 注解:
@NumberFormat(style = Style.CURRENCY) private BigDecimal price;
- 當(dāng) Spring 遇到這個(gè)字段時(shí),會(huì)查找注冊(cè)的
AnnotationFormatterFactory - 找到后調(diào)用
getPrinter()/getParser()獲取對(duì)應(yīng)的格式化工廠(chǎng) - 最終使用
CurrencyStyleFormatter來(lái)格式化金額
2. 常見(jiàn)格式化注解(在 org.springframework.format.annotation 包下)
| 注解 | 用途 |
|---|---|
@NumberFormat | 數(shù)字格式化(支持 pattern 或 style) |
@DateTimeFormat | 日期時(shí)間格式化(支持 ISO 格式、自定義 pattern) |
示例:
public class MyModel {
@DateTimeFormat(iso = ISO.DATE) // 格式:yyyy-MM-dd
private Date birthDate;
@NumberFormat(pattern = "#,###.00")
private Double salary;
}
這樣就不需要在每個(gè) controller 里手動(dòng)寫(xiě) new SimpleDateFormat(...),而是統(tǒng)一管理。
四、注冊(cè)機(jī)制 SPI:如何讓 Spring 知道你的 Formatter?
Spring 提供了幾個(gè) SPI 接口來(lái)注冊(cè)格式化器。
1. FormatterRegistry(核心注冊(cè)中心)
它是 ConverterRegistry 的擴(kuò)展,允許你注冊(cè):
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
所有 formatter/converter 都要注冊(cè)到這里才能生效。
2. FormattingConversionService(推薦實(shí)現(xiàn)類(lèi))
這是一個(gè)標(biāo)準(zhǔn)實(shí)現(xiàn),實(shí)現(xiàn)了 ConversionService + FormatterRegistry,適合大多數(shù)場(chǎng)景。
你可以:
- 編程式注冊(cè)(Java Config)
- 聲明式注冊(cè)(XML)
3. FormatterRegistrar(批量注冊(cè)工具)
有時(shí)候你需要一次性注冊(cè)多個(gè) formatter(比如所有日期相關(guān)的),但直接注冊(cè)不方便(比如類(lèi)型擦除問(wèn)題),這時(shí)可以用:
public interface FormatterRegistrar {
void registerFormatters(FormatterRegistry registry);
}
常見(jiàn)用途:
- 注冊(cè) Joda-Time 所有格式化器
- 設(shè)置全局日期格式
- 批量注冊(cè) JSR-310 (
java.time) 類(lèi)型的 formatter
五、實(shí)戰(zhàn)應(yīng)用:如何配置全局日期格式?
默認(rèn)情況下,未標(biāo)注 @DateTimeFormat 的 Date 字段會(huì)用 DateFormat.SHORT(如 4/5/25)格式化,這通常不符合中國(guó)習(xí)慣。
目標(biāo):設(shè)置全局日期格式為 yyyyMMdd
方法一:Java 配置(推薦)
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// 創(chuàng)建 ConversionService,但不注冊(cè)默認(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)項(xià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é):一句話(huà)理解全文
Spring 的 Formatter SPI 是為 客戶(hù)端環(huán)境(如 Web 頁(yè)面)設(shè)計(jì)的一套 基于 Locale 的字符串 ↔ 對(duì)象 轉(zhuǎn)換機(jī)制,相比通用的 Converter,它更適合處理用戶(hù)輸入和輸出的格式化需求,且支持注解驅(qū)動(dòng)(如 @DateTimeFormat)、可集中配置、易于擴(kuò)展。
七、常見(jiàn)問(wèn)題解答(FAQ)
Q1: Formatter 和 PropertyEditor 有什么區(qū)別?
PropertyEditor是 JavaBeans 規(guī)范的老技術(shù),線(xiàn)程不安全,API 不夠靈活。Formatter是 Spring 3 新引入的,線(xiàn)程安全、強(qiáng)類(lèi)型、支持 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 時(shí)間類(lèi)(LocalDate、LocalDateTime)支持嗎?
支持!Spring 會(huì)自動(dòng)注冊(cè) java.time 類(lèi)型的 formatter。你可以通過(guò) DateTimeFormatterRegistrar 控制其格式。
如果你正在開(kāi)發(fā) Web 應(yīng)用,尤其是需要處理表單提交、日期顯示、貨幣格式等功能,那么這一套 Formatter 機(jī)制就是你應(yīng)該掌握的核心技能之一。
需要我根據(jù)你的具體場(chǎng)景(比如 Spring Boot 項(xiàng)目)給出一個(gè)完整的配置示例嗎?
以上就是Spring處理字段格式化的實(shí)戰(zhàn)指南的詳細(xì)內(nèi)容,更多關(guān)于Spring處理字段格式化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java?Spring?AOP源碼解析之事務(wù)實(shí)現(xiàn)原理
這篇文章主要為大家介紹了Java?Spring?AOP事務(wù)實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
MyBatis在Mapper中傳遞多個(gè)參數(shù)的四種方法詳解
這篇文章主要介紹了MyBatis在Mapper中傳遞多個(gè)參數(shù)的四種方法,文章通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,,需要的朋友可以參考下2023-09-09
使用Java和WebSocket實(shí)現(xiàn)網(wǎng)頁(yè)聊天室實(shí)例代碼
WebSocket是HTML5一種新的協(xié)議,它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信,這里就將使用WebSocket來(lái)開(kāi)發(fā)網(wǎng)頁(yè)聊天室,對(duì)Java和WebSocket實(shí)現(xiàn)網(wǎng)頁(yè)聊天室的實(shí)例代碼感興趣的朋友一起學(xué)習(xí)吧2016-06-06
java讀取枚舉類(lèi)的值轉(zhuǎn)成list和map方式
這篇文章主要介紹了java讀取枚舉類(lèi)的值轉(zhuǎn)成list和map方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Spring Validation數(shù)據(jù)校驗(yàn)詳解
SpringValidation是Spring框架的數(shù)據(jù)校驗(yàn)抽象層,支持注解、分組、嵌套校驗(yàn)及國(guó)際化,默認(rèn)集成HibernateValidator,可自定義校驗(yàn)器滿(mǎn)足復(fù)雜需求,適用于Web參數(shù)校驗(yàn)和業(yè)務(wù)邏輯驗(yàn)證2025-09-09
淺談SpringBoot中的@Conditional注解的使用
這篇文章主要介紹了淺談SpringBoot中的@Conditional注解的使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04
java.sql.SQLException問(wèn)題解決以及注意事項(xiàng)
這篇文章主要給大家介紹了關(guān)于java.sql.SQLException問(wèn)題解決以及注意事項(xiàng)的相關(guān)資料,這個(gè)問(wèn)題其實(shí)很好解決,文中通過(guò)圖文將解決的辦法介紹的很詳細(xì),需要的朋友可以參考下2023-07-07
Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之食品溯源系統(tǒng)的實(shí)現(xiàn)
這是一個(gè)使用了java+Springboot+Maven+mybatis+Vue+mysql+wd開(kāi)發(fā)的食品溯源系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有食品溯源該有的所有功能,感興趣的朋友快來(lái)看看吧2022-01-01

