日期格式统一处理

时间:2024-10-19 07:10:08

这里采用实现WebMvcConfigurer接口,通过实现extendMessageConverters方法来扩展消息转换器,实现日期格式的统一处理。

1. 简单实现

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration // 表明这是一个 Spring 配置类,Spring 容器会自动扫描并加载该配置
public class WebConfig implements WebMvcConfigurer {

    /**
     * 扩展 Spring MVC 框架的消息转换器
     * 这里是通过自定义 Jackson 的 `MappingJackson2HttpMessageConverter` 来处理 JSON 数据
     * 并为其设置自定义的日期格式
     * 
     * @param converters Spring MVC 自动传入的消息转换器列表
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 输出日志,提示消息转换器正在被扩展
        log.info("扩展消息转换器...");
        
        // 创建一个 JSON 消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        
        // 创建一个 ObjectMapper(这是 Jackson 用于将 Java 对象和 JSON 相互转换的工具类)
        ObjectMapper objectMapper = new ObjectMapper();
        
        // 设置日期格式为 "yyyy-MM-dd HH:mm:ss",即:2024-10-15 12:34:56
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        
        // 将配置好的 ObjectMapper 设置到消息转换器中
        converter.setObjectMapper(objectMapper);
        
        // 将自定义的消息转换器添加到转换器列表的最前面
        // 这样 Spring MVC 会优先使用这个自定义的转换器
        converters.add(0, converter);
    }
}

1.1 主要特点

1.1.1 简单的日期格式配置

这段代码主要通过 ObjectMapper#setDateFormat 方法配置了一个全局的日期格式。它只针对日期格式进行设置,格式为 "yyyy-MM-dd HH:mm:ss"。在 JSON 数据的序列化和反序列化过程中,所有的 Date 对象都会被转换成这个格式。

1.1.2 局限性

  • 这种方式仅适用于 java.util.Date 类和类似的 java.sql.Datejava.sql.Timestamp。对于 Java 8 引入的日期时间 API(如 LocalDateTimeLocalDate)并没有处理。
  • 处理方式较为简单,只适用于某些特定场景。

1.2 适用场景

  1. 简单日期处理需求:如果项目中主要处理的是 java.util.Datejava.sql.Date 这类传统的日期时间类型,而没有使用 Java 8 引入的日期时间类(如 LocalDate, LocalDateTime),那么这段代码足以胜任。这种场景一般在老旧项目或小型应用中较为常见。

  2. 快速实现统一日期格式化:如果你仅仅需要为整个应用程序统一格式化日期,而不需要处理其他复杂的序列化和反序列化要求,这种方法能快速有效地实现这一需求。

  3. 项目简单,开发周期较短:对于一些简单的 Web 项目或 REST API,尤其是那些不涉及复杂的日期时间操作或对兼容性要求较低的场景,这段代码可以满足需求。

  4. 无复杂 JSON 序列化需求:如果你的项目对 JSON 的序列化和反序列化没有太多特殊需求,且只需要格式化日期字段,并且你不想引入太多的复杂配置,这段代码足够简单清晰。

1.3 适用环境

  • 传统的 Java Web 应用,使用的是 Java 7 或更早的版本,项目中可能没有用到 Java 8 的日期时间 API。
  • 轻量级 Web 服务,仅需要处理简单的 Date 时间格式,没有更复杂的定制化需求。

2. 全面处理 Java 日期和时间 API

2.1 实现步骤

2.1.1 自定义ObjectMapper类

在自定义的ObjectMapper类中,全面处理Java 日期和时间。

package com.sky.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

2.1.2 使用自定义的ObjectMapper

扩展消息转换器中使用自定义的ObjectMapper

public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    log.info("扩展消息转换器...");
    
    // 创建一个 JSON 消息转换器对象
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    
    // 使用自定义的 JacksonObjectMapper
    converter.setObjectMapper(new JacksonObjectMapper());
    
    // 将自定义的消息转换器添加到转换器列表的最前面
    converters.add(0, converter);
}

2.2 主要特点

  1. 全面处理 Java 日期和时间 API

    • 这段代码通过自定义的 JacksonObjectMapper 实现了对 Java 8 日期时间 API (LocalDate, LocalDateTime, LocalTime) 的序列化和反序列化处理。通过使用 SimpleModule 注册了自定义的序列化器和反序列化器,它们分别使用了特定的日期和时间格式。
    • 日期格式不仅局限于 java.util.Date,也适用于 java.time.LocalDateLocalDateTimeLocalTime
  2. 自定义反序列化特性

    • 该代码使用 FAIL_ON_UNKNOWN_PROPERTIES 关闭了对未知属性的报错。在反序列化时,如果 JSON 中包含了 Java 类中没有定义的字段,代码不会抛出异常。这使得反序列化过程更加灵活,避免因版本问题或多余字段导致的错误。
  3. 更强的灵活性

    • 通过模块化的方式,将不同的序列化器和反序列化器进行注册,支持对各种复杂对象的灵活处理。适用范围更广,特别是对 Java 8 之后的日期时间 API 处理更加完善。

2.3 适用场景

  1. 使用 Java 8 日期时间 API 的项目:如果你的项目中广泛使用了 Java 8 引入的日期时间类(如 LocalDate, LocalDateTime, LocalTime),并且需要对这些类型进行序列化和反序列化操作,第二段代码更为适合。它通过自定义 ObjectMapper 配置了对这些新日期时间类的处理。

  2. 复杂日期时间处理需求:当你需要对不同的日期和时间格式进行精细控制,比如要分别处理日期、时间和日期时间,并且希望自定义它们的格式(如将时间格式设置为 "HH:mm:ss"),第二段代码提供了更灵活的方案。

  3. 需要忽略未知属性的场景:如果你的项目可能会收到包含额外或未知字段的 JSON 数据,而你不希望反序列化时抛出异常(比如在多版本兼容的场景下),这段代码通过配置 FAIL_ON_UNKNOWN_PROPERTIES 来确保反序列化时能够兼容这些未知字段。

  4. 大型项目或微服务:在大型项目中,通常会涉及复杂的数据对象和灵活的 JSON 处理需求。此时,第二段代码的模块化扩展方式(通过 SimpleModule 注册序列化和反序列化器)使得它能够轻松处理不同的数据类型,具有较高的扩展性和灵活性。

  5. 多类型序列化需求:除了日期和时间之外,项目中可能需要对其他复杂的对象进行定制化的序列化和反序列化。这段代码通过 SimpleModule 机制,允许添加额外的序列化器和反序列化器,以满足更多复杂类型的需求。

2.4 适用环境

  • 现代 Java 应用,使用了 Java 8 及以上版本,尤其是项目中广泛使用 LocalDateTimeLocalDate 等 Java 8 日期时间类。
  • 微服务架构项目,需要对不同的服务接口返回的 JSON 进行高度灵活的序列化和反序列化处理。
  • 需要兼容多版本的项目,如果需要处理不同版本的 API 返回的数据,且 JSON 结构可能变化,此代码能够有效处理兼容性问题。
  • 大中型企业级应用,需要对序列化过程进行定制化,比如处理不同格式的日期、忽略未知字段、或对不同对象类型进行特殊处理。

3. 总结

  • 第一段代码 适合简单场景,项目对日期时间处理的要求不高,主要用于处理 Date 类的日期时间。
  • 第二段代码 适合复杂场景,尤其是使用了 Java 8 日期时间 API 的项目,它能提供更强大的自定义功能和更灵活的序列化与反序列化处理。