在 SpringMVC 開發中,前端傳遞的日期字串(如 "2023-10-01 14:30:00")自動轉換為後端 java.util.Date 或 LocalDateTime 類型,是表單提交與 REST API 的高頻需求。本文系統講解兩種主流方案:基於 @InitBinder 的表單綁定與基於 Jackson 的 JSON 格式化,並對比適用場景。
@InitBinder 標識的方法會在每次請求處理前調用,允許註冊 PropertyEditor 將請求參數(String)轉換為目標類型。Spring 內建 CustomDateEditor 專門處理日期轉換。
@Controller
@RequestMapping("/date")
public class DateBindingController {
/**
* 註冊 Date 類型的自定義編輯器
* @param binder WebDataBinder 實例
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
// 1. 定義日期格式(線程不安全,每次請求新建實例)
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false); // 嚴格模式:拒絕 "2023-13-01" 這類非法日期
// 2. 創建編輯器,allowEmpty=true 允許空字串綁定為 null
CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
// 3. 註冊:針對 java.util.Date 類型生效
binder.registerCustomEditor(Date.class, editor);
}
@PostMapping("/submit")
public String handleForm(@RequestParam("eventTime") Date eventTime, Model model) {
// 此處 eventTime 已自動完成字串 -> Date 轉換
model.addAttribute("received", eventTime);
return "result";
}
}
SimpleDateFormat 非線程安全,必須在方法內部新建實例,不可作為類字段複用。setLenient(false),防止「月份 13」、「日期 32」等非法值被自動修正為合法日期(如 2023-13-01 → 2024-01-01),導致業務數據錯誤。@InitBinder("paramName") 限定;全局統一格式建議配置 WebMvcConfigurer 的 addFormatters。LocalDateTime、LocalDate,Spring 內建 LocalDateTimeEditor 等編輯器,用法類似,格式模式遵循 DateTimeFormatter 標準(如 yyyy-MM-dd'T'HH:mm:ss)。當請求/響應體為 JSON 時(@RequestBody/@ResponseBody),SpringMVC 委託 HttpMessageConverter(默認 MappingJackson2HttpMessageConverter)進行序列化/反序列化。此時 @InitBinder 不生效,需配置 Jackson ObjectMapper。
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// 1. 設置全局日期格式(序列化輸出 & 反序列化輸入均生效)
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 或 Java 8+ 推薦:使用 ISO-8601 標準格式
// mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// mapper.registerModule(new JavaTimeModule()); // 支援 LocalDateTime 等
// 2. 設置時區(避免跨時區部署導致時間偏移)
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 或 ZoneId.of("Asia/Shanghai")
// 3. 反序列化容錯:允許單引號、忽略未知字段等
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
}
若個別字段需特殊格式,使用 @JsonFormat 覆蓋全局配置:
public class EventDTO {
// 輸入輸出均按此格式
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime startTime;
// 僅序列化輸出時格式化(如只讀字段)
@JsonFormat(pattern = "yyyy/MM/dd", shape = JsonFormat.Shape.STRING)
private LocalDate endDate;
}
| 維度 | @InitBinder + CustomDateEditor | Jackson ObjectMapper 配置 |
|---|---|---|
| 生效條件 | 表單提交(application/x-www-form-urlencoded、multipart/form-data) | JSON 請求/響應(application/json) |
| 適用類型 | java.util.Date、Calendar 等傳統類型 | 所有類型(含 Java 8+ LocalDateTime、Instant 等) |
| 配置層級 | 控制器級/方法級 | 全局級/字段級 |
| 線程安全 | 需每請求新建 SimpleDateFormat | ObjectMapper 線程安全,單例複用 |
| 推薦場景 | 傳統 MVC 表單頁、文件上傳伴隨參數 | 前後端分離 REST API、微服務間調用 |
若前端可能傳遞 "2023-10-01"、"2023-10-01 14:30"、"2023-10-01T14:30:00" 等多種格式,可自定義 JsonDeserializer:
public class FlexibleDateDeserializer extends JsonDeserializer<Date> {
private static final String[] PATTERNS = {
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy-MM-dd'T'HH:mm:ss", "yyyy/MM/dd HH:mm:ss"
};
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText().trim();
for (String pattern : PATTERNS) {
try {
return new SimpleDateFormat(pattern).parse(text);
} catch (ParseException ignored) {}
}
throw new IOException("無法解析日期格式: " + text);
}
}
// 使用:@JsonDeserialize(using = FlexibleDateDeserializer.class)
@InitBinder + CustomDateEditor,注意線程安全與嚴格模式;ObjectMapper,推薦 Java 8+ 時間 API + ISO-8601 格式;WebMvcConfigurer 或 Spring Boot spring.jackson.* 配置屬性;@JsonFormat 精細控制。掌握這兩套機制的邊界與協作,即可從容應對企業級開發中 99% 的日期綁定場景。