Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[practice-mistake] part4 #21

Open
Alice52 opened this issue Oct 29, 2021 · 1 comment
Open

[practice-mistake] part4 #21

Alice52 opened this issue Oct 29, 2021 · 1 comment
Assignees
Labels
documentation Improvements or additions to documentation java

Comments

@Alice52
Copy link
Owner

Alice52 commented Oct 29, 2021

15. Serialize & Enum

  1. redis serialize
  2. spring 中的 ObjectMapper 尽量不去覆盖之前的 Bean
    • FAIL_ON_UNKNOWN_PROPERTIES: 反序列化时忽略未知字段
    // 或者 spring.jackson.serialization.write_enums_using_index=true
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer(){
        return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
    }
  3. 默认情况下,在反序列化的时候,Jackson 框架只会调用无参构造方法创建对象
    • 如果走自定义的构造方法创建对象, 需要通过 @JsonCreator 来指定构造方法
    • 并通过 @JsonProperty 设置构造方法中参数对应的 JSON 属性名
  4. 枚举 enum
    • 客户端和服务端的枚举定义不一致时, 会出异常
    • 枚举序列化反序列化实现自定义的字段非常麻烦,会涉及 Jackson 的 Bug
  5. 使用枚举的字段交互 API
    • 设置全局的 enum 未知转默认配置: read_unknown_enum_values_using_default_value
    • 定义 enum 默认值: @JsonEnumDefaultValue UNKNOWN(-1, "未知");
    • 配置 enum 中要使用的交互字段: @JsonValue
    • 配置 restTemplate 中的 jackson converter
    • 反序列化没有使用 @JsonValue 字段: 自定义一个 EnumDeserializer, 并注册到 jackson 配置中
    • deatil code
      @Slf4j
      @Getter
      @AllArgsConstructor
      public enum StatusEnumClient {
          CREATED(1, "已创建"),
          PAID(2, "已支付"),
          DELIVERED(3, "已送到"),
          FINISHED(4, "已完成"),
          @JsonEnumDefaultValue
          UNKNOWN(-1, "未知");
      
          @JsonValue private final int status;
          private final String desc;
      }
      
       @Bean
       public Jackson2ObjectMapperBuilderCustomizer customizer() {
           return builder -> {
               builder.locale(Locale.CHINA);
               builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
               builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
               builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
               // spring.jackson.deserialization.read_unknown_enum_values_using_default_value=true
               builder.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
      
               SimpleModule module = new SimpleModule();
               module.addDeserializer(Enum.class, new EnumDeserializer());
               builder.modules(module);
           };
       }
      
       @Bean
       public RestTemplate restTemplate(MappingJackson2HttpMessageConverter converter) {
           return new RestTemplateBuilder().additionalMessageConverters(converter).build();
       }
      
       @Slf4j
       @NoArgsConstructor
       @AllArgsConstructor
       public class EnumDeserializer extends JsonDeserializer<Enum> implements ContextualDeserializer {
           private Class<Enum> targetClass;
      
           @Override
           public Enum deserialize(JsonParser p, DeserializationContext ctxt) {
               // 找枚举中带有@JsonValue注解的字段,这个字段是我们反序列化的基准字段
               Optional<Field> valueFieldOpt = Arrays.asList(targetClass.getDeclaredFields()).stream()
                       .filter(m -> m.isAnnotationPresent(JsonValue.class)).findFirst();
      
               if (valueFieldOpt.isPresent()) {
                   Field valueField = valueFieldOpt.get();
                   if (!valueField.isAccessible()) {
                       valueField.setAccessible(true);
               }
               // 遍历枚举项,查找字段的值等于反序列化的字符串的那个枚举项
               return Arrays.stream(targetClass.getEnumConstants())
                       .filter(e -> valueField.get(e).toString().equals(p.getValueAsString()))
                       .findFirst()
                       .orElseGet(
                           () -> Arrays.stream(targetClass.getEnumConstants())
                               .filter(e -> {
                                   // 如果找不到,那么就需要寻找默认枚举值来替代,同样遍历所有枚举项,查找@JsonEnumDefaultValue注解标识的枚举项
                                   return targetClass.getField(e.name()).isAnnotationPresent(JsonEnumDefaultValue.class);
                               })
                               .findFirst().orElse(null));
              }
      
              return null;
          }
      
          @Override
          public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
              targetClass = (Class<Enum>) ctxt.getContextualType().getRawClass();
              return new EnumDeserializer(targetClass);
          }
      
      }
@Alice52 Alice52 added documentation Improvements or additions to documentation java labels Oct 29, 2021
@Alice52 Alice52 self-assigned this Oct 29, 2021
@Alice52
Copy link
Owner Author

Alice52 commented Nov 1, 2021

16.LocalDateTime

  1. 时间的创建
    • new Date() 在内存中不经历格式化时就是一个时间标识[无时区概念]
    • LocalDateTime.of || ZonedDateTime.of
    • Instant -- Date
    • LocalDateTime -- Calendar
    • DateTimeFormatter -- SimpleDateFormat
  2. 时间的格式化 format
    • SimpleDateFormat: 线程不安全{DateFormat#calendar}[可以每个线程设置], pattern 不严谨, YYYY
    • LocalDateTime.parse & localDateTime.format
    • DateTimeFormatter: 线程安全是因为都是 new 的新对象, 多线程操作的是各自的对象不存在线程问题
  3. 时间的时区
    • ZonedDateTime + formatter.withZone().format()
  4. 时间的计算
    • Date() getTime 计算的话非常可能会溢出
    • Calendar.add
    • LocalDateTime
    • LocalDate.now().query() || .with()
    • Period.between 得到了两个 LocalDate; getDays() 得到的不是总天数: ChronoUnit.DAYS.between(specifyDate, today)

download (2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation java
Projects
None yet
Development

No branches or pull requests

1 participant