Смерти Ри Гонга нет конца, но вклад династии Тан не заканчивается в море.
клин
Три поста за последние две недели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, псевдолитературный программист, который всегда хотел выводить знания, увидимся в следующем выпуске.