Spring Cloud 序列化和反序列化过程定制(Jackson)

    现在都是基于Spring Cloud Feign进行微服务的调用,并且序列化的过程都封装完成的。只是自己可以定制序列化的方式,但是为了调用的时候能方便的找到问题所在等,基本都会使用json(Jackson等)方式的序列化【虽然性能比较差】。但是最近在项目上使用的时候,自己的需求是根据不同的类型(或者枚举),入参和出参会传入不同的子类,但是在接口的定义上只能使用父类进行接收。当反序列化完成后,在Controller层拿到的数据就只有父类公共的字段,而子类特有的字段在序列化时直接进行丢弃了。

    当然我完全可以用一个比较宽泛的类,放入比较全的字段,只是不同类型,有的字段可能为空,只是自己完全不想那样处理,但是处理的时候发现还是费了好多力气,于是简单记录一下定制序列化和反序列化的过程。

调用的接口:

@PostMapping("checkChain")
public CheckResultDTO checkChain(@RequestBody CheckChainDTO... checkChains);

入参对象:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonDeserialize(using = CheckChainDTOJsonDeserializer.class)
public class CheckChainDTO {

    /**
     * 验证类型
     */
    private CheckTypeEnum checkTypeEnum;

    /**
     * 入参
     */
    private CheckRequestDTO checkRequestDTO;
}

具体不同的CheckTypeEnum,会对应自己的CheckRequestDTO子类,比如(如果不自己处理的话,Spring Cloud的反序列化完成后,就会丢弃CheckRequestSkuDTO自己特有的属性skuCodeList):

public class CheckRequestSkuDTO extends CheckRequestDTO {
    /** sku编码集合 */
    private List<SkuInfoDTO> skuCodeList;
}

当然出参也是一样的,所以我是在提供feign的maven包时也提供了反序列化的过程,则进行忽略,只是两个过程都需要反序列化定制。

 

首先,先看一下项目上使用的设置Jackson为项目的序列化、反序列化方式,相信在网上也能copy到很多。如下:

@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {

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

    @Autowired
    private HttpMessageConverters httpMessageConverters;

    /**
     * MappingJackson2HttpMessageConverter 实现了HttpMessageConverter 接口,
     * httpMessageConverters.getConverters() 返回的对象里包含了MappingJackson2HttpMessageConverter
     *
     * @return
     */
    @Bean
    public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
        return new MappingJackson2HttpMessageConverter(new JacksonMapper());
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.addAll(httpMessageConverters.getConverters());
    }

    public static class JacksonMapper extends ObjectMapper {
        public JacksonMapper() {
            super();
            this.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
            this.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
            this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            this.setSerializationInclusion(JsonInclude.Include.NON_NULL);

            SimpleModule simpleModule = new SimpleModule();
            //Long 类型转成 String
            simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
            simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
            simpleModule.addSerializer(long.class, ToStringSerializer.instance);
            //日期格式化
            simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
            simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
            simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
            simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
            simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
            simpleModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

            // 定制自己的序列化方式
            simpleModule.addDeserializer(CheckChainDTO.class, new CheckChainDTOJsonDeserializer());
            registerModule(simpleModule);
        }
    }
}

 

那么我们在设置自己的序列化方式时,如上:

// 定制自己的序列化方式
simpleModule.addDeserializer(CheckChainDTO.class, new CheckChainDTOJsonDeserializer());

当然,Jackson提供了注解的方式,简单的实现,如上面直接在需要定制序列化的类上面添加的注解:

@JsonDeserialize(using = CheckChainDTOJsonDeserializer.class)

 

具体的反序列化过程类如下,枚举上直接定义了入参和出参的对应类型:

@Slf4j
public enum CheckTypeEnum {
    /** SKU检查 */
    SKU(CheckRequestSkuDTO.class, SkuResultDTO.class);

    /** 请求入参类型 */
    public final Class<? extends CheckRequestDTO> clazz;

    /** 返回值类型 */
    public final Class<? extends ResultDTO> resultClazz;

    CheckTypeEnum(Class<? extends CheckRequestDTO> clazz, 
                  Class<? extends ResultDTO> resultClazz) {
        this.clazz = clazz;
        this.resultClazz = resultClazz;
    }
}
@Slf4j
public class CheckChainDTOJsonDeserializer extends JsonDeserializer<CheckChainDTO> {
    @Override
    public CheckChainDTO deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        try {
            final String checkTypeEnumStr = node.get("checkTypeEnum").asText();
            final CheckTypeEnum checkTypeEnum = CheckTypeEnum.valueOf(checkTypeEnumStr);

            final String checkRequestDTOStr = node.get("checkRequestDTO").toString();

            final CheckRequestDTO checkRequestDTO = JSONUtil.deserializeObject(checkRequestDTOStr, checkTypeEnum.clazz);
            return new CheckChainDTO(checkTypeEnum, checkRequestDTO);
        } catch (Exception e) {
            log.error("检查接口调用链路异常:" + e);
            buildException(PARAMETER_MATCH_EXCEPTION);
        }
        return null;
    }
}

这样在项目使用的时候数据就不会丢失了,再根据具体的枚举类型,强转为对应的子类就可以了。

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Authorization Server RedisTokenStore 默认使用 JDK 序列化方式来序列化反序列化 Token,但是 JDK 序列化方式有一些缺点,比如序列化后的字节数组长度大、序列化速度慢、可读性差等。为了解决这些问题,可以使用其他序列化方式,比如 JSON 序列化方式。 具体实现步骤如下: 1. 添加 Redis 相关依赖,比如 jedis、lettuce 等。 2. 在 Spring Security 配置类中创建 RedisConnectionFactory 和 RedisTemplate,代码如下: ``` @Configuration public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory("localhost", 6379); } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } } ``` 3. 修改 TokenStore 的实现类 RedisTokenStore 中的序列化代码,将 JDK 序列化方式改为 JSON 序列化方式,代码如下: ``` public class RedisTokenStore implements TokenStore { private static final String ACCESS_TOKEN_PREFIX = "access:"; private static final String REFRESH_TOKEN_PREFIX = "refresh:"; private final RedisTemplate<String, Object> redisTemplate; public RedisTokenStore(RedisConnectionFactory connectionFactory) { Assert.notNull(connectionFactory, "connectionFactory cannot be null"); this.redisTemplate = new RedisTemplate<>(); this.redisTemplate.setConnectionFactory(connectionFactory); this.redisTemplate.setKeySerializer(new StringRedisSerializer()); this.redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); this.redisTemplate.setHashKeySerializer(new StringRedisSerializer()); this.redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); this.redisTemplate.afterPropertiesSet(); } // ... } ``` 这样就可以使用 JSON 序列化方式来序列化反序列化 Token 了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值