我们在做表单提交的时候,前台传来的数据都是 String 类型的,而 Spring 给我们提供了很多类型转换器,可以自动的将 String 类型的数据转换为 基本数据类型,但是要转换的数据不是基本类型,则 Spring将无法给我们自动转换(例如:前台传来的是日期格式的字符串,后台 使用Date类型接收,就会报错),此时需要我们编写代码将数据类型进行转换。
问题展示
表单提交
Java Bean
定义一个 bean 对象,用于接收前台传过来的数据
package com.ruoyi.system.model;import lombok.Data;import java.io.Serializable;import java.util.Date;@Datapublic class User implements Serializable {private String loginname;private Date birthday;}
Controller层
@PostMapping("/converter/user")public User testUser(User user) {System.out.println(user);return user;}
测试
启动 程序,发送请求:
前台报错:
后台打印报错信息:
Field error in object 'user' on field 'birthday': rejected value [1992-02-22]; codes [typeMismatch.user.birthday,typeMismatch.birthday,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.birthday,birthday]; arguments []; default message [birthday]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '1992-02-22'; nested exception is java.lang.IllegalArgumentException]]
方案1:转换器
Converter接口定义
从该接口的方法中,我们可以看出来是将一种类型转为另一种类型
package org.springframework.core.convert.converter;import org.springframework.lang.Nullable;@FunctionalInterfacepublic interface Converter<S, T> {@NullableT convert(S var1);}
实现Converter接口
package com.ruoyi.system.converter;import org.springframework.core.convert.converter.Converter;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;public class StringToDateConverter implements Converter<String, Date> {private static ThreadLocal<SimpleDateFormat[]> formats = new ThreadLocal<SimpleDateFormat[]>() {protected SimpleDateFormat[] initialValue() {return new SimpleDateFormat[]{new SimpleDateFormat("yyyy-MM"),new SimpleDateFormat("yyyy-MM-dd"),new SimpleDateFormat("yyyy-MM-dd HH"),new SimpleDateFormat("yyyy-MM-dd HH:mm"),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")};}};@Overridepublic Date convert(String source) {if (source == null || source.trim().equals("")) {return null;}Date result = null;String originalValue = source.trim();if (source.matches("^\\d{4}-\\d{1,2}$")) {return parseDate(source, formats.get()[0]);} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {return parseDate(source, formats.get()[1]);} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}$")) {return parseDate(source, formats.get()[2]);} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {return parseDate(source, formats.get()[3]);} else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {return parseDate(source, formats.get()[4]);} else if (originalValue.matches("^\\d{1,13}$")) {try {long timeStamp = Long.parseLong(originalValue);if (originalValue.length() > 10) {result = new Date(timeStamp);} else {result = new Date(1000L * timeStamp);}} catch (Exception e) {result = null;e.printStackTrace();}} else {result = null;}return result;}/*** 格式化日期** @param dateStr String 字符型日期* @param dateFormat 日期格式化器* @return Date 日期*/public Date parseDate(String dateStr, DateFormat dateFormat) {Date date = null;try {date = dateFormat.parse(dateStr);} catch (Exception e) {}return date;}}
将 Converter接口实现类添加到容器中
package com.ruoyi.system.config;import com.ruoyi.system.converter.StringToDateConverter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {@Beanpublic StringToDateConverter stringToDateConverter() {return new StringToDateConverter();}}
效果
前台
后端控制台:
User(loginname=zsf, birthday=Sat Feb 22 00:00:00 CST 1992)
方案2:使用@InitBinder 注解
添加依赖
在 ruoyi-common-core 模块中引入依赖
<!-- Spring Web --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><!-- Spring Context Support --><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency>
日期格式化工具类
package com.ruoyi.common.core.utils;import java.lang.management.ManagementFactory;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import org.apache.commons.lang3.time.DateFormatUtils;/*** 时间工具类** @author ruoyi*/public class DateUtils extends org.apache.commons.lang3.time.DateUtils {public static String YYYY = "yyyy";public static String YYYY_MM = "yyyy-MM";public static String YYYY_MM_DD = "yyyy-MM-dd";public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";private static String[] parsePatterns = {"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM","yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM","yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};/*** 获取当前Date型日期** @return Date() 当前日期*/public static Date getNowDate() {return new Date();}/*** 获取当前日期, 默认格式为yyyy-MM-dd** @return String*/public static String getDate() {return dateTimeNow(YYYY_MM_DD);}public static final String getTime() {return dateTimeNow(YYYY_MM_DD_HH_MM_SS);}public static final String dateTimeNow() {return dateTimeNow(YYYYMMDDHHMMSS);}public static final String dateTimeNow(final String format) {return parseDateToStr(format, new Date());}public static final String dateTime(final Date date) {return parseDateToStr(YYYY_MM_DD, date);}public static final String parseDateToStr(final String format, final Date date) {return new SimpleDateFormat(format).format(date);}public static final Date dateTime(final String format, final String ts) {try {return new SimpleDateFormat(format).parse(ts);} catch (ParseException e) {throw new RuntimeException(e);}}/*** 日期路径 即年/月/日 如2018/08/08*/public static final String datePath() {Date now = new Date();return DateFormatUtils.format(now, "yyyy/MM/dd");}/*** 日期路径 即年/月/日 如20180808*/public static final String dateTime() {Date now = new Date();return DateFormatUtils.format(now, "yyyyMMdd");}/*** 日期型字符串转化为日期 格式*/public static Date parseDate(Object str) {if (str == null) {return null;}try {return parseDate(str.toString(), parsePatterns);} catch (ParseException e) {return null;}}/*** 获取服务器启动时间*/public static Date getServerStartDate() {long time = ManagementFactory.getRuntimeMXBean().getStartTime();return new Date(time);}/*** 计算两个时间差*/public static String getDatePoor(Date endDate, Date nowDate) {long nd = 1000 * 24 * 60 * 60;long nh = 1000 * 60 * 60;long nm = 1000 * 60;// long ns = 1000;// 获得两个时间的毫秒时间差异long diff = endDate.getTime() - nowDate.getTime();// 计算差多少天long day = diff / nd;// 计算差多少小时long hour = diff % nd / nh;// 计算差多少分钟long min = diff % nd % nh / nm;// 计算差多少秒//输出结果// long sec = diff % nd % nh % nm / ns;return day + "天" + hour + "小时" + min + "分钟";}}
Controller基类
后面我们编写的所有Controller类必须继承 Controller基类,就可以拥有处理 String 转换 Date 的能力
package com.ruoyi.common.core.web.controller;import com.ruoyi.common.core.utils.DateUtils;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.InitBinder;import java.beans.PropertyEditorSupport;import java.util.Date;public class BaseController {@InitBinderpublic void initBinder(WebDataBinder binder) {// Date 类型转换binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {@Overridepublic void setAsText(String text) {setValue(DateUtils.parseDate(text));}});}}
使用
继承 BaseController即可
public class TestController extends BaseController {
效果
总结
推荐使用 方案2。原因:
①、在微服务开发的环境中,方案一需要每个独立运行的微服务模块都要在自己的模块中实现 Converter 接口。而方案二只需要在 ruoyi-common-core 模块中定义 @InitBinder 方法,然后别的模块引入 ruoyi-common-core 基础模块,最后继承 BaseController 就可以了。
②、我们编写的 BaseController 不仅可以处理 类型转换,后面在处理分页中也会用到。故此推荐方案2
补充
日期类型格式化输出前台
使用 @JsonFormat 注解标注在 java bean 的属性中,就可以
例如:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date birthday;
前台输出:
