1 2 3 4

【springMVC】全局json序列化配置

1.概述

现在我们进行web开发,一般都要设计成RESTful风格的API,通过json格式的数据进行交互。但是前端传入的 json 数据如何被解析成 Java 对象作为 API入参,后端返回结果又如何将 Java 对象解析成 json 格式数据返回给前端,在整个数据流转过程中,这是由谁来完成的呢?

SpringMVC源码中使用Jackson库或Gson库来转换json字符串,使用使用Jackson XML或者JAXB2来转换xml。

2.HttpMessageConverter简介

org.springframework.http.converter.HttpMessageConverter 是SpringMVC中提供的一个策略接口,它是一个转换器类,负责转换HTTP请求和响应,可以把对象自动转换为JSON(使用Jackson库或Gson库)或XML(使用Jackson XML或者JAXB2),对字符串默认使用UTF-8编码处理,一般情况下采用默认配置就可以。

而如果我们没有配置自己的 MessageConverter,SpringMVC 启动时就会调用 addDefaultHttpMessageConverters方法。

3.HttpMessageConverter转换原理

利用SpringMVC框架,可以使得我们在开发时,只要在代码中使用@RequestBody和@ResponseBody两个注解,就可以分别完成从请求报文到对象和从对象到响应报文的转换。而在源码内部,其实这种灵活的消息转换机制就是利用HttpMessageConverter来实现的。

 

自定义 HttpMessageConverter(一般不添加自定义,使用默认配置):

package com.ryxx.config;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ryxx.util.JacksonUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.function.Predicate;

/**
 * ClassName:JacksonObjectMapperConfig <br/>
 * Function: 一般不添加该配置,使用默认配置. <br/>
 * Reason:   一般不添加该配置,使用默认配置. <br/>
 * Date:     2023/08/17/11:31 <br/>
 *
 * @author guocheng
 * @version 1.0
 * @since JDK 1.8
 */
@Configuration
public class JacksonObjectMapperConfig extends WebMvcConfigurationSupport {


    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss.SSS}")
    private String pattern;

    @Value("${spring.jackson.time-zone:GMT+8}")
    private String timeZoneString;


    /**
     * 重定制 objectMapper
     * 想完全控制序列化过程并且不想允许外部配置时打开 @Primary 注解
     */
    @Bean(name = "objectMapper")
//    @Primary
    public ObjectMapper objectMapper() {

        ObjectMapper objectMapper = new ObjectMapper();

        // POJO对象的属性值不论是什么,序列化时都显示
        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        // 设置在反序列化时忽略在JSON字符串中存在,而在Java中不存在的属性
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 设置序列化时间类型不使用时间戳形式(jackson时间类型默认都是序列化为时间戳形式的)
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 为了处理JSON中的日期字符串并正确转换为 LocalDate,我们需要注册在 ObjectMapper 实例中注册 JavaTimeModule 模块。
        // 还需要禁止把日期转换为时间戳字符串的动作
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // Date类型序列化、反序列化全局配置-方式1
//        objectMapper.setDateFormat(new SimpleDateFormat(pattern));

        // Date类型序列化、反序列化全局配置-方式2
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneString));
        javaTimeModule.addSerializer(Date.class, new DateSerializer(false, simpleDateFormat));
        javaTimeModule.addDeserializer(Date.class, new JacksonUtils.DateDeserializer());

        // LocalDateTime类型序列化、反序列化全局配置
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern)));

        objectMapper.registerModule(javaTimeModule);

        return objectMapper;


    }

    /**
     * org.springframework.http.converter.HttpMessageConverter
     * 是SpringMVC中提供的一个策略接口,它是一个转换器类,负责转换HTTP请求和响应,
     * 可以把对象自动转换为JSON(使用Jackson库或Gson库)或XML(使用Jackson XML或者JAXB2),
     * 对字符串默认使用UTF-8编码处理,一般情况下采用默认配置就可以。
     * <p>
     * 自定义 HttpMessageConverter
     * 所以如果想要覆盖 Spring MVC 中默认的 HttpMessageConverter,可以重写 #configureMessageConverters 方法,
     * 如果只是想在默认列表中添加自定义的 HttpMessageConverter 则可以重写 #extendMessageConverters 方法。
     * <p>
     * HttpMessageConverter 同时支持请求体转换为类对象,以及将类对象转换为响应体,
     * 因此 RequestResponseBodyMethodProcessor 不管处理参数还是返回值都用到了它。
     * RequestResponseBodyMethodProcessor 将请求体转换为 Controller 方法参数,
     * 将 Controller 方法返回值转换为响应体。
     */
    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 已有的MappingJackson2HttpMessageConverter bean有可能时区不是gmt+8,直接干掉,然后添加一个新的
        converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
        // 将重定制 objectMapper 添加自定义的 HttpMessageConverter中,
        // 使 @requestBody 和 @ResponseBody 注解的序列化与反序列化生效
        converters.add(new MappingJackson2HttpMessageConverter(objectMapper()));
    }


    /**
     * 假定我们的项目中引入了 fastjson,如果我们想要配置 fastjson
     * 自带的 HttpMessageConverter 处理 application/json 内容类型,只需如下的配置。
     */
//    @Override
//    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
//        converters.add(0, converter);
//    }


}

 

 重置 jackson 默认的 ObjectMapper(一般添加该配置即可):

package com.ryxx.config;

import com.fasterxml.jackson.annotation.JsonFormat;
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.SerializationFeature;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;

/**
 * jackson全局时间格式化配置<br/>
 * springboot扫描到即生效<br/>
 * 优先级低于{@link JsonFormat}注解
 *
 * @JsonComponent 注解的类会被 SpringBoot 自动注入到默认的 ObjectMapper 中,
 * 重新定制 ObjectMapper 会覆盖该配置。
 */
@JsonComponent
public class JsonDateFormatConfig {


    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
    private String pattern;

    @Value("${spring.jackson.time-zone:Asia/Shanghai}")
    private String timeZoneString;


    @Bean
    @Primary
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {

            builder.failOnEmptyBeans(false);
            builder.failOnUnknownProperties(false);
            // 要禁用的功能(将日期写成时间戳)
            builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

            // Date类型序列化配置
            DateFormat df = new SimpleDateFormat(pattern);
            // 设置时区
            df.setTimeZone(TimeZone.getTimeZone(timeZoneString));

            // Date类型序列化、反序列化全局配置-方式1
            builder.dateFormat(df);

/*            // Date类型序列化配置(不使用时间戳)-方式2
            builder.serializerByType(Date.class, new DateSerializer(false, df));
            // Date类型反序列化配置
            builder.deserializerByType(Date.class, new JsonDeserializer<Date>() {
                @Override
                public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                    String dateStr = p.getText().trim();
                    if ("".equals(dateStr)) {
                        return null;
                    }
                    Date date = null;
                    try {
                        date = new SimpleDateFormat(pattern).parse(dateStr);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    return date;
                }
            });*/

            // LocalDateTime类型序列化配置
            builder.serializerByType(LocalDateTime.class,
                    new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern)));

            // LocalDateTime类型反序列化配置
            builder.deserializerByType(LocalDateTime.class,
                    new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern)));

            // BigDecimal类型反序列化配置
            builder.deserializerByType(BigDecimal.class, new NumberDeserializers.BigDecimalDeserializer());

        };

    }


//    /**
//     * LocalDateTime序列化器
//     */
//    @Bean
//    public LocalDateTimeSerializer localDateTimeSerializer() {
//        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
//    }
//
//    /**
//     * LocalDateTime反序列化器
//     */
//    @Bean
//    public LocalDateTimeDeserializer localDateTimeDeserializer() {
//        return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern));
//    }


}

 

 

 

 

 

 

 

 

 

 

 

参考:SpringBoot系列教程22-整合SpringMVC之HttpMessageConverters - 知乎 (zhihu.com)

SpringBoot系列教程22-整合SpringMVC之HttpMessageConverters - 知乎 (zhihu.com)

posted @ 2023-08-18 13:24  日月星宿  阅读(1176)  评论(0编辑  收藏  举报