springboot中Instant时间传参及序列化

2023-11-02

在部分场景中,后台的时间属性用的不是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;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springboot中Instant时间传参及序列化 的相关文章

  • 用vscode调试远程服务器的php

    一开始用phpstorm chrome的xdebug helper连接远程的腾讯云服务器进行xdebug调试 一直不成功 卡在 phpstorm Waiting for incoming connection with ide key xx
  • jsp Servlet的接口方法简绍

    jsp Servlet的接口方法简绍 简单介绍 三个重要的方法 重要掌握 1 被创建的方法 2 提供服务的方法 3 被销毁的方法 package Servlet import javax servlet import java io IOE
  • 使用 CentOS 7 部署前端项目

    购买服务器 以腾讯云为例 如果是 25 岁以下 可以免学生认证 使用校园优惠套餐 购买地址 云 校园 本文以 CentOS 为例 购买后 会初始化 root 密码 就可以使用云服务器了 配置服务器 购买之后就可以使用 root 用户名和腾讯
  • MySQL备份笔记

    MySQL备份笔记 备份的分类维度 备份时数据库的状态 Hot Backup 热备 正常运行中直接备份 Cold Backup 冷备 完全停止后备份 Warm Backup 温备 数据库只读 备份文件的格式 逻辑备份 输出文本或SQL语句

随机推荐

  • ubuntu 20.04 安装微信和QQ

    安装wine环境 根据教程 首先安装wine环境 然后安装weixin包 下载Wine环境包 http archive ubuntukylin com software pool partner ukylin wine 70 6 3 25
  • Linux安装配置php7+nginx

    1 安装php7 0 下载 wget http cn2 php net distributions php 7 0 4 tar gz 解压安装 tar zxvf php 7 0 4 tar gz cd php 7 0 4 首先查看安装帮助
  • vscode运行php报错php not found

    vscode运行php not found报错 要先检查原来的配置能否正常运行 1 插件 这两款插件试运行php的 2 查看php版本 3 php下载 下载php php下载 下载后配置环境变量 4 Xdebug 下载 找到生产环境 赋值一
  • vs code下运行conda环境报错

    在使用vs code下conda环境 运行程序会报告如下问题 conda activate base CommandNotFoundError Your shell has not been properly configured to u
  • 无法打开“×××”,因为无法确认开发者的身份——解决办法

    当打开这些应用程序时 系统提示无法打开 XXX 因为它来自身份不明的开发者 我们可以按照下面的方法解决 教程 1 打开应用程序 找到你要打开的软件 按住control键 点击应用打开 或者右键打开 2 出现下图所示 点击打开即可 3 只有第
  • http请求与响应,TCP三次握手&四次分手

    从前端发起请求到后台的整个过程 是一个面试中经常遇到的问题 大概的流程想必有一点基础的人都明白 但是要细说 却未必能一一道出来 曾经老师教过的知识也都差不多忘干净了 所以 我上网找了点资料 加上自己的理解 做个记录 华丽的分割线 一 HTT
  • 用Python语言开发VTK程序的步骤

    在Windows环境下用Python语言开发VTK程序 1 安装Python集成开发环境IDLE 下载地址 https www python org downloads 2 然后到VTK官网下载vtkpythone 6 2 0 Window
  • Java数据类型,强制类型转换

    1 基本数据类型 byte short int long float double char boolean 8大类型 1 整型 byte short int long 2 浮点型 float double 小数类型 3 字符型 char
  • 蓝桥杯算法提高VIP-合并石子

    题目 题解 区间dp 我是枚举的两个边界 但是出现问题了 左边界的枚举必须要从后向前 因为需要先获取区间长度小的对应的dp 才能递推出大区间的dp值 如果我左区间从左向右遍历 第二层右边界也从左向右遍历 则会先遍历到大区间 而与之相关的小区
  • vector与list的区别

    概念 vector 连续存储的容器 动态数组 在堆上分配空间 底层实现 数组 两倍容量增长 vector 增加 插入 新元素时 如果未超过当时的容量 则还有剩余空间 那么直接添加到最后 插入指定位置 然后调整迭代器 如果没有剩余空间了 则会
  • 下一步可以学下ue4 shader

    上周六 Osg直播间 恒歌提到了他们的做法 osgearth ue4 shader 这是非常好的思路 因为ue4缺点是底层资料少 优点是渲染效果好 这算是扬长避短的做法了 我正好会ue4 也会shader 正好也在学osgearth 虽然都
  • 微博数据爬虫——获取特定ID的粉丝和关注(二)

    注意 近期发现使用requests库访问微博数据出现ssl error错误 而使用urllib库访问则不会出现错误 功能 给定特定微博用户ID 获取微博用户的粉丝和关注 1 通过o id获取p id 用户主页结构如下所示 通过使用正则匹配即
  • Java远程调试

    1 把导出的jar包放到服务器上 执行的时候增加执行参数 jdk1 7版本之前的命令 java agentlib jdwp transport dt socket address 8000 server y suspend y jar xx
  • linux中病毒排查步骤,linux系统下病毒排除思路

    1 top查看是否有特别吃cpu和内存的进程 病毒进程kill是杀不死的 因为ps命令被修改 2 ls la proc 病毒进程pid pwd为病毒进程程序目录 一般在 usr bin下 3 bin ps bin netsta程序都是1 2
  • unity3d Object.Destroy 销毁

    static function Destroy obj Object t float 0 0F void Description描述 删除一个游戏物体 组件或者资源 物体obj现在被销毁或在指定了t时间过后销毁 如果obj是组件 它将从Ga
  • 深圳白领集体居家办公 远程办公或成企业新选择

    近日 深圳地区疫情爆发 多个办公大楼被划为封控区 众多白领临时接到居家办公的通知 一批又一批的员工从CBD大楼走出来 大量白领带上电脑显示屏与主机 被网友戏称 跑毒 带主机回家办公 成为突发疫情下众多人的无奈选择 远程办公成常态化 远程产品
  • STM32HAL库-移植mbedtls开源库示例(二)

    概述 本篇文章介绍如何使用STM32HAL库 这篇文章只要是讲如何使用mbedtls开源库 实现 1 base64编码 2 AES加解密示例 怎么样移植mbedtls开源库 请阅读我写的一篇文章 STM32HAL库 移植mbedtls开源库
  • ucharts饼状图文字过长超出屏幕不显示问题

    项目场景 在项目中通过饼状图对获取到的数据进行一个显示 问题描述 通过ucharts中的饼状图来进行数据的显示 通过labelText属性自定义饼状图标签文字 但发现有些数据太长 超出屏幕无法显示 如图 解决方案 1 可以进行饼状图半径的缩
  • Scanvenger游戏制作笔记(九)Unity3D创建声音

    Scanvenger游戏制作笔记 九 Unity3D创建声音 前言 一 在GameManager 上创建audio source 播放背景音乐 二 创建其他声音 三 将audioSource拖入efx source中 四 停止背景音乐 系列
  • springboot中Instant时间传参及序列化

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