详解SpringBoot时间参数处理完整解决方案

 更新时间:2019年12月02日 09:15:06   作者:空夜  
这篇文章主要介绍了详解SpringBoot时间参数处理完整解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在JavaWeb程序的开发过程中,接口是前后端对接的主要窗口,而接口参数的接收有时候是一个令人头疼的事情,这其中最困扰程序猿的,应该是时间参数的接收。

比如:设置一个用户的过期时间,前端到底以什么格式传递参数呢?时间戳?还是2019-12-01 22:13:00这种格式?还是其他格式?

今天我就来总结一下SpringBoot Web应用接口接收时间类型参数的问题解决方案。

注:目前我对Spring源码的掌握还不是很好,所以这一篇仅仅总结一下解决方法,后面感悟多了会重写一下!

示例代码请前往:https://github.com/laolunsi/spring-boot-examples

经过简单的测试,我们知道:

  1. 不使用@RequestBody注解的情况下,所有时间类型参数都会引起报错;
  2. 使用@RequestBody,前端传递时间戳或2019-11-22形式正常,传递2019-11-22 11:22:22报错,其他格式同样报错。

之前有接触过类似的解决办法,在类的属性上加上@DateFormat注解,解决单个时间参数问题。

但是局限较多。

理想的解决方案是:一次配置,全局通用,多种格式,自动转换(朗朗上口嗷)

一、源码简要分析

首先我们来简单分析一下源码:

深入的就不解释了(我现在也不懂🤦‍♂️)

简单来说,接口接收的参数,首先被HandlerMethodArgumentResolver的实现类处理了一遍,将其转换为我们需要的格式。

这里主要分为两种情况:

  • 使用了@RequestBody的参数,一般是对象接收,前端传递的通常是JSON形式
  • 其他接收参数的方式,比如@RequestAttribute,@RequestParam,或者默认形式,前端传递的通常是表单参数、请求URL后缀参数等

二、解决方法

  1. 默认形式,或使用@RequestAttribute,或使用@RequestParam,这样的参数,通过配置converter来解决问题
  2. 使用@RequestBody解析的参数,通过在ObjectMapper中配置序列化和反序列化规则来处理

2.1 自定义converter

针对第一种情况,我们需要配置converter,这里介绍两种方法:

  1. @ControllerAdvice + @InitBinder
  2. 直接使用@Bean定义converter类

首先我们这里需要一个DateConverter类,这个类实现了Converter接口,重写了其中的convert方法,将String转成Date类型:

我们这里定义了三种处理格式:

/**
 * 日期转换类
 * 将标准日期、标准日期时间、时间戳转换成Date类型
 */
/*@Deprecated*/
public class DateConverter implements Converter<String, Date> {

 private Logger logger = LoggerFactory.getLogger(DateConverter.class);

 private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
 private static final String shortDateFormat = "yyyy-MM-dd";
 private static final String timeStampFormat = "^\\d+$";

 @Override
 public Date convert(String value) {
  logger.info("转换日期:" + value);

  if(value == null || value.trim().equals("") || value.equalsIgnoreCase("null")) {
   return null;
  }

  value = value.trim();

  try {
   if (value.contains("-")) {
    SimpleDateFormat formatter;
    if (value.contains(":")) {
     formatter = new SimpleDateFormat(dateFormat);
    } else {
     formatter = new SimpleDateFormat(shortDateFormat);
    }
    return formatter.parse(value);
   } else if (value.matches(timeStampFormat)) {
    Long lDate = new Long(value);
    return new Date(lDate);
   }
  } catch (Exception e) {
   throw new RuntimeException(String.format("parser %s to Date fail", value));
  }
  throw new RuntimeException(String.format("parser %s to Date fail", value));
 }
}

注:这个DateConverter类在下面都会用到。

import com.aegis.yqmanagecenter.config.date.DateConverter;
import com.aegis.yqmanagecenter.model.bean.common.JsonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import java.beans.PropertyEditorSupport;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

@ControllerAdvice
public class ControllerHandler {

 private Logger logger = LoggerFactory.getLogger(ControllerHandler.class);

 @InitBinder
 public void initBinder(WebDataBinder binder) {
  // 方法1,注册converter
  GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService();
  if (genericConversionService != null) {
   genericConversionService.addConverter(new DateConverter());
  }

  // 方法2,定义单格式的日期转换,可以通过替换格式,定义多个dateEditor,代码不够简洁
  DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  CustomDateEditor dateEditor = new CustomDateEditor(df, true);
  binder.registerCustomEditor(Date.class, dateEditor);


  // 方法3,同样注册converter
  binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
   @Override
   public void setAsText(String text) throws IllegalArgumentException {
    setValue(new DateConverter().convert(text));
   }
  });

 }
}

注:上面的三个方法都是利用@ControllerAdvice+@InitBinder来设置时间参数处理的,其中1和3都可以设置DateConverter,而方法2只能一个一个手动设置格式。

这里需要注意,上述配置方法都无法解决Json格式数据中的时间参数接收问题。下面我们直接看完整的解决方案——将DateConverter注册为组件,并使用ObjectMapper来配置时间参数的序列化(接口返回值)和反序列化形式(接口接收参数)。

2.2 配置ObjectMapper以及完整解决方案

完整的解决方案:

/**
 * 日期转换配置
 * 解决@RequestAttribute、@RequestParam和@RequestBody三种类型的时间类型参数接收与转换问题
 */
@Configuration
public class DateConfig {

 /**
  * 默认日期时间格式
  */
 public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

 /**
  * Date转换器,用于转换RequestParam和PathVariable参数
  */
 @Bean
 public Converter<String, Date> dateConverter() {
  return new DateConverter();
 }

 /**
  * Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json
  * 使用@RequestBody注解的对象中的Date类型将从这里被转换
  */
 @Bean
 public ObjectMapper objectMapper(){
  ObjectMapper objectMapper = new ObjectMapper();
  objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

  JavaTimeModule javaTimeModule = new JavaTimeModule();

  //Date序列化和反序列化
  javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
   @Override
   public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
    String formattedDate = formatter.format(date);
    jsonGenerator.writeString(formattedDate);
   }
  });
  javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
   @Override
   public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
    return new DateConverter().convert(jsonParser.getText());
   }
  });

  objectMapper.registerModule(javaTimeModule);
  return objectMapper;
 }

}

参考:简书-Spring中使用LocalDateTime、LocalDate等参数作为入参

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java中JDBC实现动态查询的实例详解

    Java中JDBC实现动态查询的实例详解

    从多个查询条件中随机选择若干个组合成一个DQL语句进行查询,这一过程叫做动态查询。下面通过实例代码给大家讲解JDBC实现动态查询的方法,需要的朋友参考下吧
    2017-07-07
  • Java自定义实现equals()方法过程解析

    Java自定义实现equals()方法过程解析

    这篇文章主要介绍了Java自定义实现equals()方法过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • jdk自带线程池实例详解

    jdk自带线程池实例详解

    在最近做的一个项目中,需要大量的使用到多线程和线程池,下面就java自带的线程池和大家一起分享
    2018-02-02
  • MyBatis Generator 自定义生成注释的方法

    MyBatis Generator 自定义生成注释的方法

    这篇文章主要介绍了MyBatis Generator 自定义生成注释的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Java使用正则表达式提取XML节点内容的方法示例

    Java使用正则表达式提取XML节点内容的方法示例

    这篇文章主要介绍了Java使用正则表达式提取XML节点内容的方法,结合具体实例形式分析了java针对xml格式字符串的正则匹配相关操作技巧,需要的朋友可以参考下
    2017-08-08
  • java之map集合存放null值的问题

    java之map集合存放null值的问题

    这篇文章主要介绍了java之map集合存放null值的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • java实现在原有日期时间上加几个月或几天

    java实现在原有日期时间上加几个月或几天

    这篇文章主要介绍了java实现在原有日期时间上加几个月或几天,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • mysql+mybatis下全文搜索的使用方法

    mysql+mybatis下全文搜索的使用方法

    本文主要介绍了mysql+mybatis下全文搜索的使用方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Java中的TreeMap底层源码分析

    Java中的TreeMap底层源码分析

    这篇文章主要介绍了Java中的TreeMap底层源码分析,TreeMap与Hashmap、LinkedHashMap不同,他的底层不再是数组,而是一颗红黑树,在插入、删除或者替换元素时,TreeMap能按照事先约定的顺序来对key进行排序和迭代查询,需要的朋友可以参考下
    2023-12-12
  • Java实现简单的表达式计算器功能示例

    Java实现简单的表达式计算器功能示例

    这篇文章主要介绍了Java实现简单的表达式计算器功能,结合实例形式分析了Java针对输入表达式的符号分解与数值运算相关操作技巧,需要的朋友可以参考下
    2018-06-06

最新评论