Springboot中Instant时间传参及序列化详解

 更新时间:2023年11月06日 10:27:37   作者:杨某人信了你的邪  
这篇文章主要介绍了Springboot中Instant时间传参及序列化详解,Instant是Java8引入的一个精度极高的时间类型,可以精确到纳秒,但实际使用的时候不需要这么高的精确度,通常到毫秒就可以了,需要的朋友可以参考下

Instant时间传参及序列化

在部分场景中,后台的时间属性用的不是Date或Long,而是Instant,Java8引入的一个精度极高的时间类型,可以精确到纳秒,但实际使用的时候不需要这么高的精确度,通常到毫秒就可以了。

而在前后端传参的时候需要对Instant类型进行序列化及反序列化等处理,默认情况下,ObjectMapper是不支持序列化Instant类型的,需要注册JavaTimeModule才行,而且序列化的结果也不是时间戳,测试如下

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.time.Instant;

/**
 * Instant Jackson测试
 *
 * @author yangguirong
 */
@Slf4j
public class InstantTest {

    ObjectMapper objectMapper = new ObjectMapper();

    @Test
    void serializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new JavaTimeModule());
        String str = objectMapper.writeValueAsString(Instant.now());
        log.info("serializeTest: {}", str);
        // serializeTest: 1691208180.052185000
    }

    @Test
    void deserializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new JavaTimeModule());
        Instant instant = objectMapper.readValue("1691208180.052185000", Instant.class);
        log.info("deserializeTest: {}", instant);
        // deserializeTest: 2023-08-05T04:03:00.052185Z
    }
}

想要将其序列化为毫秒时间戳,需要对序列化及反序列化进行自定义

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.time.Instant;

/**
 * 自定义Instant序列化及反序列
 *
 * @author yangguirong
 */
public class InstantMillsTimeModule extends SimpleModule {

    public InstantMillsTimeModule() {
        this.addSerializer(Instant.class, new InstantMillisecondsSerializer());
        this.addDeserializer(Instant.class, new InstantMillisecondsDeserializer());
    }

    public static class InstantMillisecondsSerializer extends JsonSerializer<Instant> {
        @Override
        public void serialize(Instant instant, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (instant == null) {
                jsonGenerator.writeNull();
            } else {
                jsonGenerator.writeNumber(instant.toEpochMilli());
            }
        }
    }
    
    @Slf4j
    public static class InstantMillisecondsDeserializer extends JsonDeserializer<Instant> {
        @Override
        public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            try {
                long mills = p.getValueAsLong();
                return mills > 0 ? Instant.ofEpochMilli(mills) : null;
            } catch (Exception e) {
                log.error("Instant类型反序列化失败!val: {}, message: {}", p.getText(), e.getMessage());
            }
            return null;
        }
    }
}

再来测试一下自定义的序列化及反序列化方式

import com.example.websocket.config.InstantMillsTimeModule;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.time.Instant;

/**
 * Instant Jackson测试
 *
 * @author yangguirong
 */
@Slf4j
public class InstantTest {

    ObjectMapper objectMapper = new ObjectMapper();

    @Test
    void serializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new JavaTimeModule());
        String str = objectMapper.writeValueAsString(Instant.now());
        log.info("serialize: {}", str);
        // serialize: 1691208180.052185000
    }

    @Test
    void deserializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new JavaTimeModule());
        Instant instant = objectMapper.readValue("1691208180.052185000", Instant.class);
        log.info("deserialize: {}", instant);
        // deserialize: 2023-08-05T04:03:00.052185Z
    }

    @Test
    void millsSerializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new InstantMillsTimeModule());
        String str = objectMapper.writeValueAsString(Instant.now());
        log.info("millsSerializeTest: {}", str);
        // millsSerializeTest: 1691208541018
    }

    @Test
    void millsDeserializeTest() throws JsonProcessingException {
        objectMapper.registerModule(new InstantMillsTimeModule());
        Instant instant = objectMapper.readValue("1691208541018", Instant.class);
        log.info("millsDeserializeTest: {}", instant);
        // deserialize: 2023-08-05T04:09:01.018Z
    }
}

可以看到,结果是符合预期的,可以在毫秒时间戳和Instant之间相互转换。

在后台配置SpringBoot的时候,需要考虑两种情况,一种就是Instant作为RequestParam/PathVariable的情况,另一种是RequestBody/ResponseBody的情况。前者借助转换器实现,配置如下

import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Instant;

/**
 * web mvc设置
 *
 * @author yangguirong
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(instantConvert());
    }

    public Converter<String, Instant> instantConvert() {
        // 不能替换为lambda表达式
        return new Converter<String, Instant>() {
            @Override
            public Instant convert(String source) {
                if (StringUtils.hasText(source)) {
                    return Instant.ofEpochMilli(Long.parseLong(source));
                }
                return null;
            }
        };
    }
}

后者如果是局部配置,则在具体的实体类属性上添加@JsonSerialize和@JsonDeserialize注解,在注解中指定序列化器和反序列化器即可。如果是全局配置,则可以按照如下方式进行配置,将InstantMillsTimeModule注册为Bean,这个Bean会在JacksonAutoConfiguration中的StandardJackson2ObjectMapperBuilderCustomizer被自动注入,然后进行注册。

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Jackson配置
 *
 * @author yangguirong
 */
@Configuration
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonCustomizerConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonModuleRegistryCustomizer() {
        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.featuresToDisable(
                DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, SerializationFeature.FAIL_ON_EMPTY_BEANS
        );
    }

    @Bean
    public InstantMillsTimeModule instantMillsTimeModule() {
        return new InstantMillsTimeModule();
    }
}

简单的接口测试

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.time.Instant;

/**
 * instant测试
 *
 * @author yangguirong
 */
@Slf4j
@RequestMapping("instant")
@RestController
public class InstantTestController {

    @GetMapping("getInstant")
    public Instant getInstant() {
        return Instant.now();
    }

    @PutMapping("setInstant")
    public void setInstant(@RequestParam Instant instant) {
        log.info("setInstant: {}", instant);
    }

    @GetMapping("getInstantDemoVO")
    public DemoVO getInstantDemoVO() {
        return new DemoVO(Instant.now());
    }

    @PutMapping("setInstantDemoVO")
    public void setInstantDemoVO(@RequestBody DemoVO vo) {
        log.info("setInstantDemoVO:{}", vo);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class DemoVO {
        private Instant instant;
    }
}

到此这篇关于Springboot中Instant时间传参及序列化详解的文章就介绍到这了,更多相关Instant时间传参及序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot深入分析运行原理与功能实现

    SpringBoot深入分析运行原理与功能实现

    我们发现springBoot程序开发比spring程序编写起来容易的多。配置简洁,依赖关系简单,启动运行容易。那么结下了我们我们就要思考一下入门程序中的这些功能是怎么实现的
    2022-09-09
  • 详解SpringMVC HandlerInterceptor拦截器的使用与参数

    详解SpringMVC HandlerInterceptor拦截器的使用与参数

    本文主要介绍了详解SpringMVC HandlerInterceptor拦截器的使用与参数,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • MybatisPlus lambdaQueryWrapper中常用方法的使用

    MybatisPlus lambdaQueryWrapper中常用方法的使用

    本文主要介绍了MybatisPlus lambdaQueryWrapper中常用方法的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java中的构造方法this、super的用法详解

    Java中的构造方法this、super的用法详解

    这篇文章较详细的给大家介绍了Java中的构造方法this、super的用法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-07-07
  • 教你怎么用Java通过关键字修改pdf

    教你怎么用Java通过关键字修改pdf

    此方法只适合通过关键字位置,在pdf上添加字符直接上代码,代码比较长,大部分自己的理解都在代码注释中了,需要的朋友可以参考下
    2021-05-05
  • Java仿12306图片验证码

    Java仿12306图片验证码

    这篇文章主要为大家详细介绍了Java仿12306的图片验证码的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • Java多线程编程中synchronized关键字的基础用法讲解

    Java多线程编程中synchronized关键字的基础用法讲解

    Java的synchronized关键字用于修饰线程同步,用以线程资源共享的目的等,下面就带来简单的Java多线程编程中synchronized关键字的基础用法讲解
    2016-06-06
  • Ehcache简介_动力节点Java学院整理

    Ehcache简介_动力节点Java学院整理

    这篇文章主要介绍了Ehcache简介,使用Spring的AOP进行整合,可以灵活的对方法的返回结果对象进行缓存
    2017-07-07
  • Struts2 漏洞分析及如何提前预防

    Struts2 漏洞分析及如何提前预防

    2016年4月26日,Struts2发布一份安全公告,CVE编号 CVE-2016-3081。这是自2012年Struts2命令执行漏洞大规模爆发之后,该服务时隔四年再次爆发大规模漏洞。该漏洞也是今年目前爆出的最严重安全漏洞。本文分析了漏洞的原理危害影响防护等内容。
    2016-05-05
  • Fluent Mybatis快速入门详细教程

    Fluent Mybatis快速入门详细教程

    由于FluentMybatis是基于mybatis上做封装和扩展的,所以这里主要聊聊mybatis处理的方式,以及给出FluentMybatis的解放方案。对Fluent Mybatis入门相关知识感兴趣的朋友一起看看吧
    2021-08-08

最新评论