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模式。



相关文章
|
17小时前
|
移动开发 前端开发 NoSQL
ruoyi-nbcio从spring2.7.18升级springboot到3.1.7,java从java8升级到17(二)
ruoyi-nbcio从spring2.7.18升级springboot到3.1.7,java从java8升级到17(二)
44 0
|
17小时前
|
XML Java 数据库连接
Spring框架与Spring Boot的区别和联系
Spring框架与Spring Boot的区别和联系
15 0
|
17小时前
|
JSON Java 数据格式
nbcio-boot升级springboot、mybatis-plus和JSQLParser后的LocalDateTime日期json问题
nbcio-boot升级springboot、mybatis-plus和JSQLParser后的LocalDateTime日期json问题
|
17小时前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
17小时前
|
SQL Java 数据库连接
Springboot框架整合Spring Data JPA操作数据
Spring Data JPA是Spring基于ORM和JPA规范封装的框架,简化了数据库操作,提供增删改查等接口,并可通过方法名自动生成查询。集成到Spring Boot需添加相关依赖并配置数据库连接和JPA设置。基础用法包括定义实体类和Repository接口,通过Repository接口可直接进行数据操作。此外,JPA支持关键字查询,如通过`findByAuthor`自动转换为SQL的`WHERE author=?`查询。
|
17小时前
|
缓存 Java Maven
Spring Boot自动配置原理
Spring Boot自动配置原理
52 0
|
17小时前
|
Java 容器 Spring
Springboot自动配置原理
Springboot自动配置原理
|
17小时前
|
Java Spring 容器
SpringBoot的自动配置原理(一)
SpringBoot的自动配置原理(一)
|
17小时前
|
安全 Java Maven
Spring Boot常见企业开发场景应用、自动配置原理结构分析(三)
Spring Boot常见企业开发场景应用、自动配置原理结构分析
|
17小时前
|
Java 数据库连接 Spring
Spring Boot常见企业开发场景应用、自动配置原理结构分析(二)
Spring Boot常见企业开发场景应用、自动配置原理结构分析