我们在做表单提交的时候,前台传来的数据都是 String 类型的,而 Spring 给我们提供了很多类型转换器,可以自动的将 String 类型的数据转换为 基本数据类型,但是要转换的数据不是基本类型,则 Spring将无法给我们自动转换(例如:前台传来的是日期格式的字符串,后台 使用Date类型接收,就会报错),此时需要我们编写代码将数据类型进行转换。
问题展示
表单提交
Java Bean
定义一个 bean 对象,用于接收前台传过来的数据
package com.ruoyi.system.model;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public 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;
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T 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")
};
}
};
@Override
public 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;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public 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 {
@InitBinder
public void initBinder(WebDataBinder binder) {
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public 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;
前台输出: