Spring中处理可为Null的日期格式化及Thymeleaf表单参数问题
一、修复Null值报错
你遇到的错误本质是:前端提交了空的日期参数(比如空字符串),Spring尝试将其转换为java.util.Date时得到null,但@RequestParam默认required=true,即使参数存在但转换结果为null,依然会触发“必填参数缺失”的错误。
直接的解决方法是在@RequestParam中显式关闭必填校验,并允许参数为null:
@PostMapping("/submit-form") public String processForm( // 关闭必填校验,允许参数为null @RequestParam(name = "date_smth_form", required = false) // 指定你需要的日期格式 @DateTimeFormat(pattern = "yyyy.MM.dd") Date dateSmth, // 其他表单参数... ) { // 业务逻辑处理 return "result-page"; }
如果需要给空参数设置默认值(比如当前日期),可以加上defaultValue属性:
@RequestParam(name = "date_smth_form", required = false, defaultValue = "#{T(java.time.LocalDate).now().format(T(java.time.format.DateTimeFormatter).ofPattern('yyyy.MM.dd'))}") @DateTimeFormat(pattern = "yyyy.MM.dd") Date dateSmth
二、确保日期始终为yyyy.MM.dd格式
1. 控制器参数解析适配
把原来的@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)改成指定pattern="yyyy.MM.dd",这样Spring会严格按照你需要的格式解析前端传入的日期字符串,避免ISO标准格式(yyyy-MM-dd)的干扰。
2. 实体类字段格式化
java.util.Date本身包含时间戳,要确保只保留日期部分,需要在实体类字段上添加格式化注解,并截断时间部分:
// 实体类字段 @DateTimeFormat(pattern = "yyyy.MM.dd") private Date dateSmth; // Getter方法中截断时间,只保留日期 public Date getDateSmth() { if (dateSmth == null) return null; Calendar cal = Calendar.getInstance(); cal.setTime(dateSmth); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return cal.getTime(); } // Setter方法保持格式化规则 @DateTimeFormat(pattern = "yyyy.MM.dd") public void setDateSmth(Date dateSmth) { this.dateSmth = dateSmth; }
3. Thymeleaf前端回显适配
HTML5的type="date"输入框默认只接受yyyy-MM-dd格式的字符串,回显时需要把实体类的Date对象格式化为该格式,确保前端能正确显示:
<input type="date" th:field="*{dateSmth}" th:value="${#dates.format(dateSmth, 'yyyy-MM-dd')}" />
提交时前端会自动把日期转为yyyy-MM-dd字符串,后端通过@DateTimeFormat(pattern="yyyy.MM.dd")解析成Date对象,两者格式不冲突,Spring的日期解析器会正确处理转换逻辑。
三、可选:全局日期格式化配置
如果项目中有大量日期参数需要统一格式,可以配置全局规则,避免每个参数都加注解:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd"); registry.addFormatter(new DateFormatter() { @Override public Date parse(String text, Locale locale) throws ParseException { if (text == null || text.trim().isEmpty()) { return null; } LocalDate localDate = LocalDate.parse(text, formatter); return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); } @Override public String print(Date date, Locale locale) { if (date == null) { return ""; } LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); return formatter.format(localDate); } }); } }
配置后,控制器中的@RequestParam Date参数会自动按照yyyy.MM.dd格式解析,无需再添加@DateTimeFormat注解。
内容的提问来源于stack exchange,提问作者Sergei

