Spring Boot升级到2.x,Jackson对Date时间类型序列化的变化差点让项目暴雷【享学Spring Boot】(中)

简介: Spring Boot升级到2.x,Jackson对Date时间类型序列化的变化差点让项目暴雷【享学Spring Boot】(中)

Rest表现(@ResponseBody)

在web层(其实为Spring MVC),对于Rest接口,默认会使用Jackson进行消息的序列化。那么它在不同版本的表现也会存在差异:


公用代码:

@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/get")
    public Object get() {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("date", new Date());
        map.put("timestamp", new Timestamp(System.currentTimeMillis()));
        map.put("localDateTime", LocalDateTime.now());
        map.put("localDate", LocalDate.now());
        map.put("localTime", LocalTime.now());
        map.put("instant", Instant.now());
        return map;
    }
}


1.x版本:
{
    "date":1580897613003,
    "timestamp":1580897613003,
    "localDateTime":{
        "dayOfMonth":5,
        "dayOfWeek":"WEDNESDAY",
        "month":"FEBRUARY",
        "year":2020,
        "hour":18,
        "minute":13,
        "nano":9000000,
        "second":33,
        "dayOfYear":36,
        "monthValue":2,
        "chronology":{
            "id":"ISO",
            "calendarType":"iso8601"
        }
    },
    "localDate":{
        "year":2020,
        "month":"FEBRUARY",
        "dayOfMonth":5,
        "dayOfWeek":"WEDNESDAY",
        "era":"CE",
        "chronology":{
            "id":"ISO",
            "calendarType":"iso8601"
        },
        "dayOfYear":36,
        "leapYear":true,
        "monthValue":2
    },
    "localTime":{
        "hour":18,
        "minute":13,
        "second":33,
        "nano":9000000
    },
    "instant":{
        "epochSecond":1580897613,
        "nano":9000000
    }
}
2.x版本:


{
    "date":"2020-02-02T13:26:07.116+0000",
    "timestamp":"2020-02-02T13:26:07.116+0000",
    "localDateTime":"2020-02-02T21:26:07.12",
    "localDate":"2020-02-02",
    "localTime":"21:26:07.12",
    "instant":"2020-02-02T13:26:07.120Z"
}


小总结


Rest表现处理啊的差异,完全同容器内的ObjectMapper的差异。

根据前面掌握的知识:Spring MVC消息转换器使用的ObjectMapper实例是自己新构建的,和容器内的无关,但为何Spring Boot里的表现是如此呢?详细缘由,接下来会做出解答。


Spring Boot消息转换器配置与Jackson


从现象上看,Spring Boot使用的ObjectMapper是从容器中拿的,而传统Spring MVC使用的是自己新构建的。此处存在差异,需要一探究竟。同样的逆推法,一切还是从MappingJackson2HttpMessageConverter出发,Spring Boot使用了一个JacksonHttpMessageConvertersConfiguration配置类来配置Jackson的消息转换器。


JacksonHttpMessageConvertersConfiguration

Configuration for HTTP message converters that use Jackson.



@Configuration
class JacksonHttpMessageConvertersConfiguration {
  // 目的:向容器内扔一个MappingJackson2HttpMessageConverter实例,但是有很多约束条件
  @Configuration
  @ConditionalOnClass(ObjectMapper.class)
  @ConditionalOnBean(ObjectMapper.class)
  @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, havingValue = "jackson", matchIfMissing = true)
  protected static class MappingJackson2HttpMessageConverterConfiguration {
    @Bean
    @ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
        ignoredType = { "org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
            "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
      return new MappingJackson2HttpMessageConverter(objectMapper);
    }
  }
  ... // 支持xml,略
}


该配置的目的是向Spring容器内放置一个Jackson消息转换器实例,不过它有很多前提条件:


  1. 导入了Jackson核心包,并且容器内存在ObjectMapper这个Bean
  2. spring.http.converters.preferred-json-mapper这个key对应的值不能是false(缺少此key默认也是true)
  3. 你自己木有定义MappingJackson2HttpMessageConverter这个Bean,这个内置的会生效


这些条件在Spring Boot下只要导入了Jackson核心包就自然而然的成立了。从源码处很清楚了:MappingJackson2HttpMessageConverter它使用的是Spring容器内的ObjectMapper完成的构建。


那么JacksonHttpMessageConvertersConfiguration此配置类如何被最终使用的呢?这个很关键,因此这里大体倒退一下,列出如下:


@Configuration
@ConditionalOnClass(HttpMessageConverter.class)
@AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class })
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class })
public class HttpMessageConvertersAutoConfiguration {
  // 把容器内所有的消息转换器注入、管理起来
  private final List<HttpMessageConverter<?>> converters;
  public HttpMessageConvertersAutoConfiguration(ObjectProvider<List<HttpMessageConverter<?>>> convertersProvider) {
    this.converters = convertersProvider.getIfAvailable();
  }
  // 向容器内扔一个`HttpMessageConverters`实例,管理所有的HttpMessageConverter
  // HttpMessageConverters它实现了接口:Iterable
  // 本处:converters的值有两个(size为2):`MappingJackson2HttpMessageConverter`和`StringHttpMessageConverter`
  @Bean
  @ConditionalOnMissingBean
  public HttpMessageConverters messageConverters() {
    return new HttpMessageConverters((this.converters != null) ? this.converters : Collections.<HttpMessageConverter<?>>emptyList());
  }
  ... // 略。 -> 向容器内定义一个StringHttpMessageConverter,用于处理字符串消息
}


由以上源码可知:


  • EnableAutoConfiguration驱动HttpMessageConvertersAutoConfiguration生效,它通过@Import让JacksonHttpMessageConvertersConfiguration配置生效。
  • 默认情况下容器内通过@Bean方式配置了两个消息转换器:MappingJackson2HttpMessageConverter和StringHttpMessageConverter,最后都封装进HttpMessageConverters实例里,此实例也放进了容器。


所以,其它组件若要使用消息转换器,只需要“引入”HttpMessageConverters这个Bean来使用即可。有两个地方使用到了它:WebMvcAutoConfiguration和WebClientAutoConfiguration,分别对应Servlet和Reactive模式。



相关文章
|
4月前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
149 0
|
4月前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
150 0
|
3月前
|
前端开发 Java Maven
Spring 和 Spring Boot 之间的比较
本文对比了标准Spring框架与Spring Boot的区别,重点分析两者在模块使用(如MVC、Security)上的差异。Spring提供全面的Java开发基础设施支持,包含依赖注入和多种开箱即用的模块;而Spring Boot作为Spring的扩展,通过自动配置、嵌入式服务器等功能简化开发流程。文章还探讨了两者的Maven依赖、Mvc配置、模板引擎配置、启动方式及打包部署等方面的异同,展示了Spring Boot如何通过减少样板代码和配置提升开发效率。总结指出,Spring Boot是Spring的增强版,使应用开发、测试与部署更加便捷高效。
386 12
|
4月前
|
消息中间件 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——发布/订阅消息的生产和消费
本文详细讲解了Spring Boot中ActiveMQ的发布/订阅消息机制,包括消息生产和消费的具体实现方式。生产端通过`sendMessage`方法发送订阅消息,消费端则需配置`application.yml`或自定义工厂以支持topic消息监听。为解决点对点与发布/订阅消息兼容问题,可通过设置`containerFactory`实现两者共存。最后,文章还提供了测试方法及总结,帮助读者掌握ActiveMQ在异步消息处理中的应用。
136 0
|
4月前
|
消息中间件 网络协议 Java
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ集成
本文介绍了在 Spring Boot 中集成 ActiveMQ 的详细步骤。首先通过引入 `spring-boot-starter-activemq` 依赖并配置 `application.yml` 文件实现基本设置。接着,创建 Queue 和 Topic 消息类型,分别使用 `ActiveMQQueue` 和 `ActiveMQTopic` 类完成配置。随后,利用 `JmsMessagingTemplate` 实现消息发送功能,并通过 Controller 和监听器实现点对点消息的生产和消费。最后,通过浏览器访问测试接口验证消息传递的成功性。
117 0
|
2月前
|
存储 安全 IDE
说一说序列化与反序列化中存在的问题
本文详细解析了Java中的序列化机制,包括序列化的概念、实现方式及应用场景。通过Student类的实例演示了对象的序列化与反序列化过程,并分析了`Serializable`接口的作用以及`serialVersionUID`的重要意义。此外,文章还探讨了如何通过自定义`readObject()`方法增强序列化的安全性,以及解决可序列化单例模式中可能产生的多实例问题。最后提供了代码示例和运行结果,帮助读者深入理解序列化的原理与实践技巧。
|
2月前
|
JSON JavaScript 前端开发
Go语言JSON 序列化与反序列化 -《Go语言实战指南》
本文介绍了 Go 语言中使用 `encoding/json` 包实现 JSON 与数据结构之间的转换。内容涵盖序列化(`Marshal`)和反序列化(`Unmarshal`),包括基本示例、结构体字段标签的使用、控制字段行为的标签(如 `omitempty` 和 `-`)、处理 `map` 和切片、嵌套结构体序列化、反序列化未知结构(使用 `map[string]interface{}`)以及 JSON 数组的解析。最后通过表格总结了序列化与反序列化的方法及类型要求,帮助开发者快速掌握 JSON 数据处理技巧。
|
8月前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
205 1
|
8月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
9月前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。