详解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等参数作为入参

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

(0)

相关推荐

  • springboot json时间格式化处理的方法

    application.properties中加入如下代码 springboot 默认使用 jackson 解析 json spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 如果个别实体需要使用其他格式的 pattern,在实体上加入注解即可 import org.springframework.format.annotation.DateTimeFormat; import com.fas

  • spring boot实战教程之shiro session过期时间详解

    前言 众所周知在spring boot内,设置session过期时间只需在application.properties内添加server.session.timeout配置即可.在整合shiro时发现,server.session.timeout设置为7200,但未到2小时就需要重新登录,后来发现是shiro的session已经过期了,shiro的session过期时间并不和server.session.timeout一致,目前是采用filter的方式来进行设置. ShiroSessionFil

  • SpringBoot设置接口超时时间的方法

    SpringBoot设置接口访问超时时间有两种方式 一.在配置文件application.properties中加了spring.mvc.async.request-timeout=20000,意思是设置超时时间为20000ms即20s, 二.还有一种就是在config配置类中加入: public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureAsyncSupport(fin

  • springboot使用JPA时间类型进行模糊查询的方法

    这个问题是我自己开发中遇到的问题  数据库使用的是mysql5.6  字段名称为checkingTime  类型为timestamp 显而易见 存到库中的是保留6位毫秒 即yyyy-MM-dd HH:mm:ss.ssssss  此时需求是精确到分钟的相同时间 不进行存储 这时候就需要进行模糊查询   搜了一圈百度 并没有什么好用的方法 我的bean类定义的是date类型 使用注解将类型更改为timestamp 存入库中 其实在做模糊查询的时候  只需要向持久层传入String类型参数即可 我的做

  • spring boot aop 记录方法执行时间代码示例

    本文研究的主要是spring boot aop 记录方法执行时间的实现代码,具体如下. 为了性能调优,需要先统计出来每个方法的执行时间,直接在方法前后log输出太麻烦,可以用AOP来加入时间统计 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency&

  • 解决Spring Boot和Feign中使用Java 8时间日期API(LocalDate等)的序列化问题

    LocalDate . LocalTime . LocalDateTime 是Java 8开始提供的时间日期API,主要用来优化Java 8以前对于时间日期的处理操作.然而,我们在使用Spring Boot或使用Spring Cloud Feign的时候,往往会发现使用请求参数或返回结果中有 LocalDate . LocalTime . LocalDateTime 的时候会发生各种问题.本文我们就来说说这种情况下出现的问题,以及如何解决. 问题现象 先来看看症状.比如下面的例子: @Sprin

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

    在JavaWeb程序的开发过程中,接口是前后端对接的主要窗口,而接口参数的接收有时候是一个令人头疼的事情,这其中最困扰程序猿的,应该是时间参数的接收. 比如:设置一个用户的过期时间,前端到底以什么格式传递参数呢?时间戳?还是2019-12-01 22:13:00这种格式?还是其他格式? 今天我就来总结一下SpringBoot Web应用接口接收时间类型参数的问题解决方案. 注:目前我对Spring源码的掌握还不是很好,所以这一篇仅仅总结一下解决方法,后面感悟多了会重写一下! 示例代码请前往:ht

  • 详解SpringBoot中的参数校验(项目实战)

    Java后端发工作中经常会对前端传递过来的参数做一些校验,在业务中还要抛出异常或者不断的返回异常时的校验信息,充满了if-else这种校验代码,在代码中相当冗长.例如说,用户注册时,会校验手机格式的正确性,用户名的长度等等.虽说前端也可以做参数校验,但是为了保证我们API接口的可靠性,以保证最终数据入库的正确性,后端进行参数校验不可忽视. Hibernate Validator 提供了一种统一方便的方式,让我们快速的实现参数校验. Hibernate Validator 使用注解,实现声明式校验

  • 详解SpringBoot基于Dubbo和Seata的分布式事务解决方案

    1. 分布式事务初探 一般来说,目前市面上的数据库都支持本地事务,也就是在你的应用程序中,在一个数据库连接下的操作,可以很容易的实现事务的操作. 但是目前,基于SOA的思想,大部分项目都采用微服务架构后,就会出现了跨服务间的事务需求,这就称为分布式事务. 本文假设你已经了解了事务的运行机制,如果你不了解事务,那么我建议先去看下事务相关的文章,再来阅读本文. 1.1 什么是分布式事务 对于传统的单体应用而言,实现本地事务可以依赖Spring的@Transactional注解标识方法,实现事务非常简

  • 详解springboot设置默认参数Springboot.setDefaultProperties(map)不生效解决

    我们都知道springboot 由于内置tomcat(中间件)直接用启动类就可以启动了. 而且我们有时想代码给程序设置一些默认参数,所以使用方法Springboot.setDefaultProperties(map) SpringApplication application = new SpringApplication(startClass); // Map<String, Object> params = new HashMap<>(); params.put("l

  • 详解SpringBoot配置文件启动时动态配置参数方法

    序言 当我们要同时启用多个项目而又要使用不同端口或者变换配置属性时,我们可以在配置文件中设置${变量名}的变量来获取启动时传入的参数,从而实现了动态配置参数,使启用项目更加灵活 例子 server: port: ${PORT:50101} #服务端口 spring: application: name: xc‐govern‐center #指定服务名 eureka: client: registerWithEureka: true #服务注册,是否将自己注册到Eureka服务中 fetchReg

  • 详解springboot整合ehcache实现缓存机制

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. ehcache提供了多种缓存策略,主要分为内存和磁盘两级,所以无需担心容量问题. spring-boot是一个快速的集成框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 由于spring-boot无需任何样板化的配置文件,所以spring-boot集成一些其他框架时会有略微的

  • 详解SpringBoot上传图片到阿里云的OSS对象存储中

    启动idea创建一个SpringBoot项目 将上面的步骤完成之后,点击下一步创建项目 创建完成之后修改pom.xml文件,添加阿里云oss依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional&g

  • 详解SpringBoot项目的创建与单元测试

    前言   Spring Boot 设计之初就是为了用最少的配置,以最快的速度来启动和运行 Spring 项目.Spring Boot使用特定的配置来构建生产就绪型的项目. Hello World 1.可以在 Spring Initializr上面添加,也可以手动在 pom.xml中添加如下代码∶ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>Spring-boot-s

  • 详解Springboot下载Excel的三种方式

    汇总一下浏览器下载和代码本地下载实现的3种方式. (其实一般都是在代码生成excel,然后上传到oss,然后传链接给前台,但是我好像没有实现过直接点击就能在浏览器下载的功能,所以这次一起汇总一下3种实现方式.)

  • 详解Springboot整合ActiveMQ(Queue和Topic两种模式)

    写在前面: 从2018年底开始学习SpringBoot,也用SpringBoot写过一些项目.这里对学习Springboot的一些知识总结记录一下.如果你也在学习SpringBoot,可以关注我,一起学习,一起进步. ActiveMQ简介 1.ActiveMQ简介 Apache ActiveMQ是Apache软件基金会所研发的开放源代码消息中间件:由于ActiveMQ是一个纯Java程序,因此只需要操作系统支持Java虚拟机,ActiveMQ便可执行. 2.ActiveMQ下载 下载地址:htt

随机推荐