Изучение сериализации глобальной согласованности из сериализации LocalDateTime

Java
Изучение сериализации глобальной согласованности из сериализации LocalDateTime

Смерти Ри Гонга нет конца, но вклад династии Тан не заканчивается в море.

клин

Три поста за последние две неделиSpringSecurityиэссе, Я планирую написать несколько простых, полезных и простых в использовании статей на этой неделе, передумаю и сделаю перерыв.

Сегодня я напишу следующее:从LocalDateTime序列化来看全局一致性序列化体验.

Это название не кажется очень человеческим, и оно имеет очень официальное ощущение.Позвольте мне сначала перевести и перевести то, что наша тема: объясняяLocalDateTimeСериализация для выявления всех процессов сериализации в рамках проекта и обеспечения их согласованности.

Обычно в нашем проекте есть два вида сериализации:

одинSpringMVCОфициальная сериализация, котораяSpringСериализация, которую вы делаете для вас, например, вы вводите интерфейс на интерфейсеResponseBodyаннотация,SpringMVCКонвертер сообщений выполнит сериализацию за вас.

Другой — это сериализация в нашем проекте, будь то JsonUtil, определенный вами самостоятельно, или сторонний инструмент обработки JSON, который вы представили (например,FastJson) или можно сказать, что это сериализация внутри нашего проекта.

Если они разные, иногда сериализованные данные могут иметь разные результаты.Чтобы предотвратить это, сегодня мы обсудим сериализацию в проекте.

1. 💡 Приведите пример

Давайте сначала рассмотрим пример, чтобы увидеть, что произойдет, если сериализация несовместима.

@GetMapping("/api/anon")
    public ApiResult test01() {
        return ApiResult.ok("匿名访问成功");
    }

Это очень распространенный интерфейс доступа, и возвращаемые результаты выглядят следующим образом:

{
    "code": 200,
    "msg": "请求成功",
    "data": {
        "请求成功": "匿名访问成功"
    },
    "timestamp": "2020-07-19T23:07:07.738",
    "fail": false,
    "success": true
}

Здесь просто нужно обратить вниманиеtimestampРезультат сериализации ,timestampЯвляетсяLocalDateTimeвведитеSpringMVCпара преобразователей сообщений вLocalDateTimeПри сериализации нет специальной обработки, и она вызывается напрямуюLocalDateTime**toString()**, так что есть середина этого результата сериализацииT.

Но если сериализация здесь использует другие решения, результат сериализации может быть другим опытом Я также использую его в своем проекте.Jacksonсериализовать (SpringОн также используется в), мы можем посмотреть на пару JsonUtil, определенную намиLocalDateTimeЧто будет результатом выполнения сериализации.

@Slf4j
public class JacksonUtil {

    public static ObjectMapper objectMapper = new ObjectMapper();


    /**
     * Java对象转JSON字符串
     *
     * @param object
     * @return
     */
    public static String toJsonString(Object object) {
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("The JacksonUtil toJsonString is error : \n", e);
            throw new RuntimeException();
        }
    }
}

Наш класс инструмента сериализации выглядит так, как и выше, мы сериализуемApiResultПосмотрите, что происходит:

{
    "code": 400,
    "msg": "请求失败",
    "timestamp": {
        "month": "JULY",
        "year": 2020,
        "dayOfMonth": 19,
        "hour": 23,
        "minute": 25,
        "monthValue": 7,
        "nano": 596000000,
        "second": 2,
        "dayOfYear": 201,
        "dayOfWeek": "SUNDAY",
        "chronology": {
            "id": "ISO",
            "calendarType": "iso8601"
        }
    },
    "fail": true,
    "success": false
}

существуетJacksonдефолтObjectMapperРезультатом следующей сериализации является этот призрак, потому что он сериализуется и, наконец, преобразуется в строку, поэтому, если такой интерфейс данных получен, он не должен нормально преобразовываться в тип времени.

LocalDateTimeЭто всего лишь микрокосм. Даже для строк разные конфигурации сериализации имеют разный эффект. В строке могут быть escape-символы и кавычки. Результаты разных схем могут быть разными.

В реальных проектах обычно требуется HTTP-стыковка сторонних интерфейсов.Данные, переданные в прошлом, обычно сериализуются в строки с помощью класса инструментов JSON в нашем проекте, а затем передаются.Если схема сериализации отличается, это может быть данные, которые будут переданы в процессе сериализации, — это не то, что нам нужно.

Также есть некоторые интерфейсы, к которым мы напрямую переходимHttpServeletResponseВ него записываются данные, и в это время обычно записываются данные JSON, такие как:

public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setHeader("Cache-Control", "no-cache");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().println(JacksonUtil.toJsonString(ApiResult.fail(authException.getMessage())));
        response.getWriter().flush();
    }

Здесь я использую класс инструмента, чтобы сериализовать это напрямуюApiResult, данные, переданные на стойку регистрации, также появятся в приведенном выше примере,LocalDateTimeСериализованный результат — это не то, что нам нужно.

Итак, сериализация в проекте иSpringПо-прежнему необходимо поддерживать согласованность сериализации в .

2. 📃Практичное решение

Выше было сказано о необходимости поддержания согласованности сериализации в проекте (я думаю, что это необходимо, ха-ха).

Тогда мы можем поговорить о том, как сделать эту согласованность ниже.

Мы знаем, что если вы хотитеSpringСериализация объекта, который вы возвращаете, будет определеннойLocalDateTimeЕсли переменная типа сериализована, это очень просто, вы можете сделать это:

public class ApiResult implements Serializable {

    private static final Map<String, String> map = new HashMap<>(1);
    private int code;
    private String msg;
    private Object data;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime timestamp;

Очень просто добавить переменную к этой переменнойJsonFormatАннотация в порядке, но это не глобально, независимо от того, какая переменная добавлена ​​к какой переменной, вступит в силу.

Чтобы сделать его эффективным в глобальном масштабе, нам необходимоSpringконфигурация для измененияSpringиспользуется вObjectMapper, друзья, которые знают Джексона, должны знать, что различные конфигурации сериализации настраиваются в этомObjectMapperНеважно, если вы не знаете, теперь вы знаете.

Затем мы можем настроить его с помощьюSpringсерединаObjectMapperСделайте его эффективным во всем мире:

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            builder.locale(Locale.CHINA);
            builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
            builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");

            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

            builder.modules(javaTimeModule);
        };
    }
}    

вJackson2ObjectMapperBuilderCustomizerЭтого эффекта можно добиться, добавив некоторые схемы сериализации.Приведенный выше код выполняет эти операции, поэтому при повторном доступе к первому интерфейсу появятся следующие эффекты:

{
    "code": 200,
    "msg": "请求成功",
    "data": {
        "请求成功": "匿名访问成功"
    },
    "timestamp": "2020-07-20 00:06:12",
    "fail": false,
    "success": true
}

timestampСредней Т больше не существует, потому что мы уже присоединилисьLocalDateTimeсхема сериализации.

Но этого недостаточно, это просто сделаноLocalDateTimeГлобальная сериализация , нам также нужно сделать наш инструментальный класс также иSpringсогласуется с:

    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
    {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();

        // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的
        // Include.NON_NULL 属性为NULL 不序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 允许出现特殊字符和转义符
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        // 允许出现单引号
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        /**
         *  将Long,BigInteger序列化的时候,转化为String
         */
//  SimpleModule simpleModule = new SimpleModule();
//
//  simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
//  simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
//  simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
//
//  objectMapper.registerModule(simpleModule);

        // 将工具类中的 objectMapper 换为 Spring 中的 objectMapper
        JacksonUtil.objectMapper = objectMapper;
        return objectMapper;
    }

Этот код следует за предыдущим шагом, правильноJackson2ObjectMapperBuilderстроительObjectMapperВыполните некоторые манипуляции и установите ряд свойств, которые вы хотите.

Закомментированная часть кода также является преобразованием сериализации.Если вы используете длинный номер типа LONG в своем проекте, это может привести к тому, что JS не получит полный номер, потому что длинный тип в java длиннее числового типа в JS One. точка, в это время вы должны преобразовать его в String на стойке регистрации, чтобы он мог получить правильный номер.Если вам это нужно, вы можете открыть этот раздел.

Последняя фраза, к чему мы более критичны, выходит строительObjectMapperПрисвойте значение нашему классу инструментовObjectMapper, в этом случае они фактически указывают на адрес, то есть используют один и тот же объект для сериализации, и результат, конечно же, один и тот же.

постскриптум

сегодняшний从LocalDateTime序列化探讨全局一致性序列化Вот и все, надеюсь всем поможет.

Я также поместил код этой статьи в предыдущийSpringSecruityВ демо вы можете напрямую перейти к нему и найти имя класса, чтобы найти его.

Код для этой статьи: Адрес облака кодаАдрес GitHub

Смерти Ри Гонга нет конца, но вклад династии Тан не заканчивается в море.

Каждый ваш лайк, подборка и комментарий - отличное подтверждение моих знаний.Если есть какие-то ошибки или сомнения в тексте, или какой-то совет для меня, вы можете оставить сообщение под областью комментариев и обсудить вместе.

Я Ear, псевдолитературный программист, который всегда хотел выводить знания, увидимся в следующем выпуске.