②优雅的缓存框架:SpringCache的序列化配置优化

97 阅读1分钟

SpringCache的集成redis序列化格式优化

redis序列化和反序列化

采用Jackson实现【数据库】-【redis】-【客户端】数据结构一致性

public class JacksonRedisSerializer<T> implements RedisSerializer<T> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public JacksonRedisSerializer(JavaTimeModule module) {
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        objectMapper.registerModule(module);
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        try {
            // 序列化时 不写出类型信息,只写出与返回到前端一直的格式, 实现数据库-redis-客户端数据一致性
            // 若写出类型信息:如
            return objectMapper.writeValueAsBytes(t);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        // 反序列化时,通过获取方法返回值的泛型声明信息构造JavaType,传递给Jackson反序列化
        Type returnType = CacheResultTypeContext.getReturnType();
        if (returnType == null) {
            throw new RuntimeException("请先配置返回值类型");
        }

        JavaType javaType = objectMapper.constructType(returnType);

        try {
            return objectMapper.readValue(bytes, javaType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

携带类型信息的JSON

{
  "@class": "cn.dev33.satoken.dao.SaSessionForJacksonCustomized",
  "id": "token:login:session:1",
  "createTime": 1694179443112,
  "dataMap": {
    "@class": "java.util.concurrent.ConcurrentHashMap"
  },
  "tokenSignList": [
    "java.util.Vector",
    [
      {
        "@class": "cn.dev33.satoken.session.TokenSign",
        "value": "5zcJ4CrzcfqxPH5ODigwhCAbHZYGKo5T",
        "device": "default-device"
      }
    ]
  ]
}

不携带类型信息的JSON

[
  {
    "id": "1",
    "createBy": "1",
    "createTime": "2023-08-10 22:50:29.000",
    "updateBy": "1",
    "updateTime": "2023-08-10 22:50:28.000",
    "name": "系统管理员",
    "category": "默认",
    "orderNo": 1,
    "status": 1
  }
]

采用AOP拦截并保存方法返回类型信息


@Component
@Aspect
@Order(-1) // 需要比redis serializer 提前执行
public class CacheResultTypeAop {

    // CacheResultType用于标识方法返回值类型信息
    @Around(value = "@annotation(org.springframework.cache.annotation.Cacheable) || @annotation(org.springframework.cache.annotation.CachePut) || @annotation(org.springframework.cache.annotation.Caching)")
    public Object run(JoinPoint joinPoint) throws Throwable {
        Type returnType = null;
        if (joinPoint.getSignature() instanceof MethodSignature signature) {
            // 获取返回类型 并 携带泛型信息
            returnType = signature.getMethod().getGenericReturnType(); 
        }
        try {
            CacheResultTypeContext.setType(returnType);
            return ((MethodInvocationProceedingJoinPoint) joinPoint).proceed(joinPoint.getArgs());
        } finally {
            CacheResultTypeContext.remove();
        }
    }
}

下一篇: ③优雅的缓存框架:SpringCache增强支持自定义过期时间TTL

以上代码来源: 后端代码:github.com/L1yp/van-te…

前端代码:github.com/L1yp/van-te…

点击链接加入群聊:【Van交流群】