Spring MVC 綁定請求參數到命令對象/實體類時,基本類型(int、double、boolean)與日期類型無法直接從字符串自動轉換,會導致綁定失敗或拋出 TypeMismatchException。
@Controller 或 @ControllerAdvice 中定義PropertyEditorpublic class BaseController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
// 日期類型:支持多種格式
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
private final String[] patterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy/MM/dd"};
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.trim().isEmpty()) { setValue(null); return; }
for (String pattern : patterns) {
try {
setValue(new SimpleDateFormat(pattern).parse(text.trim()));
return;
} catch (ParseException ignored) {}
}
throw new IllegalArgumentException("Invalid date format: " + text);
}
@Override public String getAsText() {
Date value = (Date) getValue();
return value != null ? new SimpleDateFormat(patterns[0]).format(value) : "";
}
});
// Integer:空字符串轉 0
binder.registerCustomEditor(Integer.class, new PropertyEditorSupport() {
@Override public void setAsText(String text) {
setValue(text == null || text.isEmpty() ? 0 : Integer.parseInt(text.trim()));
}
});
// Double:空字符串轉 0.0
binder.registerCustomEditor(Double.class, new PropertyEditorSupport() {
@Override public void setAsText(String text) {
setValue(text == null || text.isEmpty() ? 0.0 : Double.parseDouble(text.trim()));
}
});
// Boolean:支持 true/false、yes/no、on/off、1/0
binder.registerCustomEditor(Boolean.class, new PropertyEditorSupport() {
@Override public void setAsText(String text) {
if (text == null) { setValue(false); return; }
String t = text.trim().toLowerCase();
setValue("true".equals(t) || "yes".equals(t) || "on".equals(t) || "1".equals(t));
}
});
}
}
@InitBinder("user") // 對應 @ModelAttribute("user") User user
public void initUserBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, "birthDate", new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd"), true));
}
@ControllerAdvice
public class GlobalBindingConfig {
@InitBinder
public void globalBinder(WebDataBinder binder) {
// 所有 Controller 共享
binder.registerCustomEditor(...);
}
}
@Component
public class CustomDateFormatter implements Formatter {
@Override public Date parse(String text, Locale locale) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd").parse(text);
}
@Override public String print(Date object, Locale locale) {
return new SimpleDateFormat("yyyy-MM-dd").format(object);
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new CustomDateFormatter());
registry.addFormatter(new CustomNumberFormatter()); // 統一數字格式
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 日期綁定失敗 | 格式不匹配 | 註冊多格式支持的 PropertyEditor |
| 空字符串報錯 | 基本類型無法接收 null | 轉為包裝類型或註冊編輯器默認值 |
| 枚舉綁定失敗 | 默認只支持名稱匹配 | 註冊支持代碼/值匹配的編輯器 |
| 集合泛型丟失 | 類型擦除 | 使用 @InitBinder 註冊 CollectionPropertyEditor |
掌握 @InitBinder,徹底解決「前端傳什麼、後端怎麼收」的類型轉換煩惱,讓控制器專注業務邏輯。