SpringBoot中請(qǐng)求入?yún)㈩愋娃D(zhuǎn)換的6種玩法
做 Spring Boot Web 開發(fā)的小伙伴,想必都做過請(qǐng)求入?yún)㈩愋娃D(zhuǎn)換吧?比如前端傳過來(lái)的日期字符串(像 "2026-02-11"),后端用 Date 類型接收;前端傳的數(shù)字字符串(像 "1,000"),轉(zhuǎn)成 Integer 類型……
以前遇到這種情況,咱們大概率會(huì)寫一堆 @InitBinder 方法,手動(dòng)配置類型轉(zhuǎn)換器,代碼又多又繁瑣,還不好維護(hù)。但今天我要告訴你:**別再死磕 @InitBinder 做日期轉(zhuǎn)換了!**用 Spring 提供的 @DateTimeFormat 注解,就能輕松搞定,省時(shí)又省力~
一、核心注解:不止 @DateTimeFormat,還有這些“神器”
除了咱們剛才說(shuō)的 @DateTimeFormat(專門處理日期/時(shí)間類型轉(zhuǎn)換),Spring 還貼心提供了 @NumberFormat 注解,用來(lái)處理數(shù)字類型的入?yún)⑥D(zhuǎn)換,這兩個(gè)注解堪稱表單請(qǐng)求的“黃金搭檔”,上手零難度。
1. 表單請(qǐng)求入?yún)⑥D(zhuǎn)換:6種方式,按需選擇不踩坑
表單請(qǐng)求(比如 GET 請(qǐng)求的參數(shù)、POST 表單提交的參數(shù))的入?yún)⑥D(zhuǎn)換,Spring 提供了多種方案,從簡(jiǎn)單注解到自定義擴(kuò)展,覆蓋所有場(chǎng)景,咱們按“簡(jiǎn)單到復(fù)雜”的節(jié)奏一步步說(shuō):
最便捷:@DateTimeFormat 處理日期/時(shí)間
這是目前最常用、最簡(jiǎn)潔的日期轉(zhuǎn)換方式,無(wú)需額外配置,直接在實(shí)體類屬性或方法參數(shù)上添加注解,指定日期格式即可。
完整代碼示例(兩種用法全覆蓋):
1.方法參數(shù)用法(GET請(qǐng)求):
// Controller 方法
@RequestMapping("/getDate")
public ResponseEntity<String> getDate(
// 前端傳參:?date=2026-02-11
@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
return ResponseEntity.ok("轉(zhuǎn)換后的日期:" + date);
}
2.實(shí)體類屬性用法(POST表單提交):
// 實(shí)體類
public class UserForm {
// 前端表單提交:date=2026-02-11
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
// 其他屬性 + getter/setter
}
// Controller 方法
@RequestMapping("/submitForm")
public ResponseEntity<String> submitForm(UserForm userForm) {
Date birthday = userForm.getBirthday();
return ResponseEntity.ok("轉(zhuǎn)換后的生日:" + birthday);
}
同系列:@NumberFormat 處理數(shù)字格式
和 @DateTimeFormat 用法類似,專門解決前端數(shù)字字符串(帶千分位、百分比等)轉(zhuǎn)后端數(shù)字類型的問題。
完整代碼示例(結(jié)合實(shí)體類,貼合實(shí)際開發(fā)):
// 表單實(shí)體類(接收POST表單/GET參數(shù))
public class ProductForm {
// 前端傳參:num=1,000(千分位字符串)
@NumberFormat(pattern = "#,###")
private Integer num;
// 前端傳參:rate=50%(百分比字符串)
@NumberFormat(pattern = "0.00%")
private Double rate;
// getter/setter 方法
public Integer getNum() { return num; }
public void setNum(Integer num) { this.num = num; }
public Double getRate() { return rate; }
public void setRate(Double rate) { this.rate = rate; }
}
// Controller 方法
@RequestMapping("/product")
public ResponseEntity<Map<String, Object>> getProduct(ProductForm form) {
Map<String, Object> result = new HashMap<>();
result.put("轉(zhuǎn)換后數(shù)量", form.getNum()); // 1000(Integer類型)
result.put("轉(zhuǎn)換后比例", form.getRate()); // 0.5(Double類型)
return ResponseEntity.ok(result);
}
說(shuō)明:前端傳參無(wú)論是 GET(?num=1,000&rate=50%)還是 POST 表單提交,Spring 都會(huì)自動(dòng)通過注解完成轉(zhuǎn)換。
更靈活:自定義注解
如果 @DateTimeFormat 和 @NumberFormat 滿足不了需求(比如自定義特殊格式、多格式兼容),可以使用 自定義注解 + AnnotationFormatterFactory 方式
核心邏輯:AnnotationFormatterFactory 是 Spring 提供的核心接口,專門用于**“綁定自定義注解與類型轉(zhuǎn)換邏輯”**。當(dāng)Spring檢測(cè)到參數(shù)/屬性上有自定義注解時(shí),會(huì)通過該工廠直接執(zhí)行轉(zhuǎn)換邏輯
完整代碼示例(支持多日期格式):
1.自定義注解(標(biāo)記需要轉(zhuǎn)換的字段,定義可配置的日期格式):
// 自定義日期轉(zhuǎn)換注解(核心:定義支持的多格式)
@Target({ElementType.FIELD, ElementType.PARAMETER}) // 可用于實(shí)體屬性和方法參數(shù)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomDateTimeFormat {
// 默認(rèn)支持兩種格式,可根據(jù)需求自定義修改
String[] patterns() default {"yyyy-MM-dd", "yyyy/MM/dd"};
}
2.核心實(shí)現(xiàn):AnnotationFormatterFactory
實(shí)現(xiàn)AnnotationFormatterFactory接口,在接口方法中編寫轉(zhuǎn)換邏輯
// 核心類:關(guān)聯(lián)自定義注解 + 實(shí)現(xiàn)轉(zhuǎn)換邏輯,無(wú)需依賴Formatter接口
public class CustomDateTimeFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<CustomDateTimeFormat> {
// 第一步:指定該工廠支持轉(zhuǎn)換的目標(biāo)類型(此處僅支持Date類型)
@Override
public Set<Class<?>> getFieldTypes() {
Set<Class<?>> fieldTypes = new HashSet<>();
fieldTypes.add(Date.class); // 明確適配Date類型,避免無(wú)效轉(zhuǎn)換
return fieldTypes;
}
// 第二步:實(shí)現(xiàn)“格式化輸出”邏輯(可選,根據(jù)業(yè)務(wù)需求實(shí)現(xiàn))
@Override
public Printer<Date> getPrinter(CustomDateTimeFormat annotation, Class<?> fieldType) {
return (date, locale) -> {
if (date == null) {
return "";
}
// 使用注解中配置的第一個(gè)格式作為輸出格式
SimpleDateFormat sdf = new SimpleDateFormat(annotation.patterns()[0]);
return sdf.format(date);
};
}
// 第三步:核心實(shí)現(xiàn)“入?yún)⒔馕觥边壿嫞ㄇ岸俗址?→ 后端Date)
@Override
public Parser<Date> getParser(CustomDateTimeFormat annotation, Class<?> fieldType) {
return (text, locale) -> {
if (text == null || text.trim().isEmpty()) {
return null; // 空值處理,避免空指針
}
String[] supportPatterns = annotation.patterns();
// 遍歷注解中支持的所有格式,嘗試解析前端傳參
for (String pattern : supportPatterns) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setLenient(false); // 嚴(yán)格校驗(yàn)格式,避免非法日期(如2026-02-30)
return sdf.parse(text.trim());
} catch (ParseException e) {
continue; // 一種格式失敗,嘗試下一種
}
}
// 所有格式均解析失敗,拋出明確異常,便于排查問題
throw new ParseException(
"日期格式錯(cuò)誤,支持的格式:" + Arrays.toString(supportPatterns),
text.length()
);
};
}
}
3.全局注冊(cè)工廠(一次配置,全局生效):
將AnnotationFormatterFactory注冊(cè)到Spring容器中,無(wú)需額外配置,所有添加了@CustomDateTimeFormat注解的參數(shù)/屬性均可自動(dòng)完成轉(zhuǎn)換:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 注冊(cè)自定義注解工廠,替代傳統(tǒng)的Formatter注冊(cè),全局生效
registry.addFormatterForFieldAnnotation(new CustomDateTimeFormatAnnotationFormatterFactory());
}
}
4.使用方式(和@DateTimeFormat完全一致,簡(jiǎn)潔易用):
無(wú)論是Controller方法參數(shù),還是實(shí)體類屬性,只需添加自定義注解,即可自動(dòng)完成轉(zhuǎn)換,無(wú)需額外代碼:
// 用法1:Controller方法參數(shù)(GET請(qǐng)求,前端傳參:?date=2026/02/11 或 ?date=2026-02-11)
@RequestMapping("/customDate")
public ResponseEntity<String> customDate(
@CustomDateTimeFormat(patterns = {"yyyy-MM-dd", "yyyy/MM/dd"}) Date date) {
return ResponseEntity.ok("轉(zhuǎn)換成功:" + date);
}
// 用法2:實(shí)體類屬性(POST表單提交,前端傳參:birthday=2026-02-11)
public class UserForm {
// 無(wú)需額外配置,注解貼哪里,轉(zhuǎn)換就生效哪里
@CustomDateTimeFormat
private Date birthday;
// getter/setter 方法(必須提供,Spring需通過反射獲取/設(shè)置屬性值)
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
}
通用適配:實(shí)現(xiàn)Formatter接口(全局類型轉(zhuǎn)換)
除了注解方式,表單請(qǐng)求還可通過直接實(shí)現(xiàn)Formatter接口,配置全局類型轉(zhuǎn)換規(guī)則,無(wú)需在參數(shù)/實(shí)體上添加任何注解,適合同一類型(如數(shù)字、日期)需統(tǒng)一轉(zhuǎn)換格式的場(chǎng)景。
核心邏輯:Formatter接口是Spring表單請(qǐng)求的通用轉(zhuǎn)換接口,專門處理**“前端字符串 → 后端目標(biāo)類型”的轉(zhuǎn)換,實(shí)現(xiàn)接口后注冊(cè)到Spring容器,即可對(duì)指定類型的所有表單入?yún)?*自動(dòng)生效,無(wú)需額外綁定注解。
完整代碼示例(以數(shù)字格式轉(zhuǎn)換為例,適配所有Integer類型表單入?yún)ⅲ?/p>
1.實(shí)現(xiàn)Formatter接口,編寫數(shù)字轉(zhuǎn)換邏輯(千分位字符串轉(zhuǎn)Integer):
// 實(shí)現(xiàn)Formatter接口,指定轉(zhuǎn)換類型為Integer(前端千分位字符串 → 后端Integer)
public class IntegerFormatter implements Formatter<Integer> {
@Override
public Integer parse(String text, Locale locale) throws ParseException {
if (text == null || text.trim().isEmpty()) {
return null;
}
// 處理前端千分位字符串(如"1,000" → 1000)
try {
// 去除千分位逗號(hào),轉(zhuǎn)換為Integer
String numStr = text.replace(",", "");
return Integer.parseInt(numStr);
} catch (NumberFormatException e) {
throw new ParseException("數(shù)字格式錯(cuò)誤,支持格式:帶千分位或普通數(shù)字(如1,000、1000)", 0);
}
}
@Override
public String print(Integer object, Locale locale) {
// 格式化輸出(可選,用于后端向前端返回時(shí)的格式處理)
if (object == null) {
return "";
}
// 后端返回時(shí),自動(dòng)格式化為千分位字符串
return String.format("%,d", object);
}
}
2.注冊(cè)Formatter到Spring容器,全局生效:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 注冊(cè)自定義IntegerFormatter,全局所有Integer類型表單入?yún)⒆詣?dòng)適配
registry.addFormatter(new IntegerFormatter());
// 可同時(shí)注冊(cè)多個(gè)Formatter,適配不同類型(如Date、Double等)
}
}
3.使用方式(無(wú)需注解,自動(dòng)適配):
前端傳參(GET/POST表單):?num=1,000 或 ?num=2000,后端直接用Integer接收,無(wú)需添加@NumberFormat注解:
// Controller方法(無(wú)需任何轉(zhuǎn)換注解)
@RequestMapping("/formatterNum")
public ResponseEntity<String> formatterNum(Integer num) {
// 前端傳"1,000",自動(dòng)轉(zhuǎn)換為1000(Integer類型)
return ResponseEntity.ok("Formatter轉(zhuǎn)換后:" + num);
}
// 實(shí)體類屬性(無(wú)需注解,同樣自動(dòng)轉(zhuǎn)換)
public class OrderForm {
// 前端傳num=3,500,自動(dòng)轉(zhuǎn)換為3500
private Integer num;
// getter/setter方法
public Integer getNum() { return num; }
public void setNum(Integer num) { this.num = num; }
}
關(guān)鍵說(shuō)明:
- 此方式無(wú)注解依賴,注冊(cè)后對(duì)指定類型(如示例中的Integer)的所有表單入?yún)⑷稚?,適合項(xiàng)目中同一類型需統(tǒng)一轉(zhuǎn)換格式的場(chǎng)景;
- 可實(shí)現(xiàn)多個(gè)Formatter,分別適配Date、Double等不同類型,覆蓋多種表單轉(zhuǎn)換需求;
- 與@NumberFormat注解對(duì)比:注解方式靈活(可單獨(dú)配置單個(gè)參數(shù)/屬性格式),F(xiàn)ormatter方式通用(全局統(tǒng)一格式),按需選擇即可。
古老但可用:@InitBinder + PropertyEditor
這是 Spring 早期的日期/類型轉(zhuǎn)換方式,也是咱們以前常用的“笨辦法”——通過 @InitBinder 注解,在 Controller 中手動(dòng)注冊(cè) PropertyEditor 轉(zhuǎn)換器,指定日期格式。
缺點(diǎn)很明顯:代碼繁瑣,每個(gè) Controller 都要寫一遍(除非抽成全局?jǐn)r截器),維護(hù)成本高;優(yōu)點(diǎn)是兼容性好,適合老項(xiàng)目改造,不建議新項(xiàng)目使用。
完整代碼示例(傳統(tǒng) @InitBinder 用法):
@Controller
@RequestMapping("/old")
public class OldConverterController {
// 每個(gè)Controller都要寫一遍,繁瑣!
@InitBinder
public void initBinder(WebDataBinder binder) {
// 注冊(cè)日期轉(zhuǎn)換器,指定格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// 嚴(yán)格校驗(yàn)格式,不匹配則報(bào)錯(cuò)
dateFormat.setLenient(false);
// 綁定到PropertyEditor
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
// 前端傳參:?date=2026-02-11
@RequestMapping("/date")
public ResponseEntity<String> getDate(Date date) {
return ResponseEntity.ok("轉(zhuǎn)換后的日期:" + date);
}
}
說(shuō)明:若要全局生效,可將 @InitBinder 方法抽成 BaseController,所有 Controller 繼承 BaseController,避免重復(fù)編碼,但仍不如全局 Formatter 簡(jiǎn)潔。
特殊場(chǎng)景:自定義 Converter(僅適用于方法參數(shù))
除了 Formatter,Spring 還提供了 Converter 接口 用于類型轉(zhuǎn)換,但要注意一個(gè)關(guān)鍵細(xì)節(jié):Converter 不能用于實(shí)體類屬性轉(zhuǎn)換,只能用于 Controller 方法的單個(gè)參數(shù)轉(zhuǎn)換。
適用場(chǎng)景:比如前端傳一個(gè)特殊格式的字符串(如 "2026|02|11"),需要轉(zhuǎn)成 Date 類型,用 Converter 實(shí)現(xiàn)轉(zhuǎn)換邏輯,再注冊(cè)到 Spring 容器中,即可直接在方法參數(shù)上使用。
完整代碼示例(自定義 Converter,僅適用于方法參數(shù)):
1.實(shí)現(xiàn) Converter 接口,編寫轉(zhuǎn)換邏輯:
// 自定義Converter:String(2026|02|11)→ Date
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source == null || source.isEmpty()) {
return null;
}
try {
// 適配前端特殊格式:2026|02|11
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.parse(source);
} catch (ParseException e) {
throw new IllegalArgumentException("日期格式錯(cuò)誤,正確格式:yyyy|MM|dd");
}
}
}
2.注冊(cè) Converter 到 Spring 容器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
// 注冊(cè)自定義Converter
registry.addConverter(new StringToDateConverter());
}
}
3.使用方式(僅支持方法參數(shù),實(shí)體類屬性無(wú)效):
// 前端傳參:?date=2026|02|11
@RequestMapping("/converterDate")
public ResponseEntity<String> converterDate(Date date) {
// 無(wú)需加任何注解,Spring自動(dòng)調(diào)用Converter轉(zhuǎn)換
return ResponseEntity.ok("Converter轉(zhuǎn)換成功:" + date);
}
關(guān)鍵提醒:若將 Date 類型放在實(shí)體類中,前端傳參后 Converter 不會(huì)生效,仍需用 Formatter 或 @DateTimeFormat 注解。
2. JSON 請(qǐng)求入?yún)⑥D(zhuǎn)換:Jackson 注解搞定一切
現(xiàn)在大部分項(xiàng)目都是前后端分離,常用 JSON 格式傳參(比如 POST 請(qǐng)求的 RequestBody),這種場(chǎng)景下,上面說(shuō)的 Formatter、Converter 就失效了,此時(shí)需要用 Jackson 相關(guān)注解 來(lái)完成類型轉(zhuǎn)換。
Jackson 是 Spring Boot 默認(rèn)的 JSON 解析工具,提供了豐富的注解,覆蓋日期、數(shù)字、布爾值等所有常見類型的轉(zhuǎn)換,用法和前面的 Spring 注解類似,直接貼在實(shí)體類屬性上即可。
常用 Jackson 轉(zhuǎn)換注解(必記):
- @JsonFormat:最常用,處理日期/時(shí)間轉(zhuǎn)換,支持指定格式、時(shí)區(qū)(解決時(shí)區(qū)偏移問題),比如 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
- @JsonDeserialize:自定義反序列化器,用于復(fù)雜類型轉(zhuǎn)換(比如自定義字符串轉(zhuǎn)枚舉、轉(zhuǎn)復(fù)雜對(duì)象)
- @JsonNumberFormat:處理 JSON 中的數(shù)字格式轉(zhuǎn)換(類似 @NumberFormat,但僅適用于 JSON 請(qǐng)求)
注意:JSON 請(qǐng)求的轉(zhuǎn)換,核心是“Jackson 反序列化”,所有注解都是作用于 Jackson 的反序列化過程,和表單請(qǐng)求的 Formatter 是兩個(gè)完全獨(dú)立的體系,不要混淆哦~
二、總結(jié):入?yún)⑥D(zhuǎn)換選型指南,按需Pick不踩坑
看完上面的內(nèi)容,相信你已經(jīng)清楚不同場(chǎng)景下該用哪種入?yún)⑥D(zhuǎn)換方式了,這里做個(gè)總結(jié),幫你快速選型,提高開發(fā)效率:
表單請(qǐng)求(GET/POST 表單):
- 簡(jiǎn)單日期/數(shù)字轉(zhuǎn)換:優(yōu)先用 @DateTimeFormat、@NumberFormat(最便捷)
- 復(fù)雜格式/多格式兼容:用 自定義注解 或 Formatter(靈活)
- 老項(xiàng)目兼容:用 @InitBinder + PropertyEditor(不推薦新項(xiàng)目)
- 單個(gè)方法參數(shù)特殊轉(zhuǎn)換:用自定義 Converter
JSON 請(qǐng)求(@RequestBody):
- 簡(jiǎn)單日期/數(shù)字轉(zhuǎn)換:用 @JsonFormat、@JsonNumberFormat
- 復(fù)雜類型轉(zhuǎn)換:用 @JsonDeserialize + 自定義反序列化器
最后補(bǔ)充一句:建議在項(xiàng)目中統(tǒng)一配置全局轉(zhuǎn)換規(guī)則(比如全局日期格式、全局?jǐn)?shù)字格式),避免每個(gè)地方重復(fù)配置,提升代碼可維護(hù)性。
從此,告別繁瑣的 @InitBinder,用對(duì)注解和自定義方式,Spring Boot Web 入?yún)⑥D(zhuǎn)換就能高效又省心!
以上就是SpringBoot中請(qǐng)求入?yún)㈩愋娃D(zhuǎn)換的6種玩法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot請(qǐng)求入?yún)㈩愋娃D(zhuǎn)換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實(shí)現(xiàn)24點(diǎn)紙牌游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)24點(diǎn)紙牌游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
SpringBoot整合mybatis-generator-maven-plugin的方法
這篇文章主要介紹了SpringBoot整合mybatis-generator-maven-plugin,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
SpringCloud實(shí)現(xiàn)權(quán)限管理(網(wǎng)關(guān)+jwt版)
本文主要介紹了SpringCloud實(shí)現(xiàn)權(quán)限管理(網(wǎng)關(guān)+jwt版),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-10-10
教你怎么在IDEA中創(chuàng)建java多模塊項(xiàng)目
這篇文章主要介紹了教你怎么在IDEA中創(chuàng)建java多模塊項(xiàng)目,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
詳解Java8合并兩個(gè)Map中元素的正確姿勢(shì)
這篇文章主要介紹了詳解Java8合并兩個(gè)Map中元素的正確姿勢(shì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
在Java中高效實(shí)現(xiàn)并發(fā)訪問控制的全過程
現(xiàn)代軟件開發(fā)中,尤其是在高并發(fā)的系統(tǒng)架構(gòu)中,如何處理并發(fā)訪問問題,確保系統(tǒng)的穩(wěn)定性和性能,是每個(gè)開發(fā)者必須掌握的技能,本文將深入探討如何在Java中高效實(shí)現(xiàn)并發(fā)訪問控制,我們將逐步介紹常見的并發(fā)控制工具,需要的朋友可以參考下2025-09-09

