redis 反序列化 isSuccess()序列化失败问题

前言

公司统一封装了返回bash对象R<T> 其中有个方法 isCuccess 来判断接口是否成功。由于性能优化把部分接口返回的对象给缓存了 R<Object>存储在redis 序列化成功,但是反序列化失败了。我们来看下具体问题

定位问题

R<T> 对象 重点在 isSuccess 方法

scss 复制代码
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {

    /**
     * 成功标记
     */
    public static final Integer SUCCESS = 200;
    /**
     * 失败标记
     */
    public static final Integer FAIL = -1;
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    /**
     * code
     */
    private Integer code;
    /**
     * msg
     */
    private String msg;

    /**
     * data
     */
    private T data;

    /**
     * currentTimeMills
     */
    @Builder.Default
    private Long currentTimeMillis = System.currentTimeMillis();

    public static <T> R<T> ok() {
        return restResult(null, SUCCESS, null);
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, SUCCESS, null);
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, SUCCESS, msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, FAIL, null);
    }

    public static <T> R<T> failed(String msg) {
        return restResult(null, FAIL, msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, FAIL, null);
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, FAIL, msg);
    }

    public static <T> R<T> restResult(T data, Integer code, String msg) {
        return R.<T>builder().code(code).data(data).msg(msg).build();
    }

    public static R<Void> failure(Integer code, String message) {
        return R.<Void>builder().code(code).msg(message).build();
    }

    /**
     * @return boolean
     */
    public boolean isSuccess() {
        return SUCCESS.equals(code);
    }
}

redis 存储的数据

json 复制代码
[
  "com.wf.cabbage.web.base.R",
  {
    "code": 200,
    "msg": null,
    "data": [
      "java.util.ArrayList",
      []
    ],
    "currentTimeMillis": 1712662374888,
    "success": true
  }
]

错误

text 复制代码
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized field "success" (class com.xx.cabbage.web.base.R), not marked as ignorable (4 known properties: "msg", "data", "currentTimeMillis", "code"])
 at [Source: (byte[])"["com.xx.cabbage.web.base.R",{"code":200,"msg":null,"data":["java.util.ArrayList",[]],"currentTimeMillis":1712662374888,"success":true}]"; line: 1, column: 135] (through reference chain: com.xx.cabbage.web.base.R["success"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "success" (class com.xx.cabbage.web.base.R), not marked as ignorable (4 known properties: "msg", "data", "currentTimeMillis", "code"])
 at [Source: (byte[])"["com.xx.cabbage.web.base.R",{"code":200,"msg":null,"data":["java.util.ArrayList",[]],"currentTimeMillis":1712662374888,"success":true}]"; line: 1, column: 135] (through reference chain: com.xx.cabbage.web.base.R["success"])

解决

这是因为 redis 我们这边使用的序列化是 GenericJackson2JsonRedisSerializer 使用的是 Jackson 库进行序列化和反序列化,而 Jackson 库默认会将 Java 对象中的 getter 方法转换为 JSON 对象中的属性。因此,在反序列化 Redis 中的 JSON 数据时,Jackson 库会将 isSuccess() 方法转换为 JSON 对象中的 success 属性。

要解决这个问题,你可以采用以下两种方式之一:

  1. 在 R 类中添加一个名为 "success" 的 getter 方法,并在该方法中调用 isSuccess() 方法。这样,在反序列化 Redis 中的 JSON 数据时,Jackson 库就可以正确地将 success 属性映射到 Java 对象的 isSuccess() 方法中。
  2. 修改 GenericJackson2JsonRedisSerializer 的配置,禁用 Jackson 库的 getter 方法转换功能。具体来说,你可以通过以下代码来创建 GenericJackson2JsonRedisSerializer 对象: 核心配置 objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
ini 复制代码
ObjectMapper objectMapper = new ObjectMapper();

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 支持 java 1.8+ LocalDateTime 序列化
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =
       new GenericJackson2JsonRedisSerializer(objectMapper);

this.setDefaultSerializer(genericJackson2JsonRedisSerializer);

this.setKeySerializer(new StringRedisSerializer());

this.setHashKeySerializer(new StringRedisSerializer());
this.setHashValueSerializer(genericJackson2JsonRedisSerializer);

this.setValueSerializer(genericJackson2JsonRedisSerializer);
相关推荐
Bill FANG17 分钟前
从原生Java NIO到Netty重构:充电桩Socket服务端高性能改造实战
java·重构·nio
2601_9557594118 分钟前
Claude API 成本审计:该看哪些指标,怎么分析更靠谱
java
带刺的坐椅28 分钟前
跳出 Fatjar 的束缚:Solon 插件的体外扩展(E-Spi)与热插拔(H-Spi)
java·jar·solon·plugin·fatjar
Csvn30 分钟前
📊 SQL 入门 Day 14:聚合窗口函数 — 让 SUM / AVG 也能"滑动"起来
后端·sql
Oneslide31 分钟前
K8s NodePort 端口为什么 netstat 查不到?
后端
北冥you鱼35 分钟前
Go语言大数(big.Int)比较大小:原理、方法与最佳实践
开发语言·后端·golang
宁之明起(B服)35 分钟前
Java 反序列化漏洞-xmldecoder/SnakeYAM
java·开发语言
kkkAloha44 分钟前
非对称加解密理解
后端
xcLeigh1 小时前
Go入门:零值与变量默认初始化机制
开发语言·后端·golang
禾高网络1 小时前
开启环保新潮流:回收小程序系统功能介绍
java·大数据·小程序