jetcache List 缓存, json 序列化 泛型解析成了 JsonObject 处理

1. 如果缓存的value 是一个list, jetcache json 序列化 无法解析泛型具体类型

  • 可以看到, ArrayList 的元素是 JsonObject
  • 正常使用是没有问题, 但是如果使用 JDK stream 流编程的话, 就会出现对象转换异常

2. 常规解决办法, JSON 序列化指定 class类型

Encode

  • 存在 "@type" 注入安全问题
java 复制代码
public class JetCacheFastjson2ValueEncoder extends Fastjson2ValueEncoder {

    public JetCacheFastjson2ValueEncoder(boolean useIdentityNumber) {
        super(useIdentityNumber);
    }

    @Override
    public byte[] apply(Object value) {
        return super.apply(value);
    }

    @Override
    protected byte[] encodeSingleValue(Object value) {
        return JSON.toJSONString(value, JSONWriter.Feature.WriteClassName).getBytes();
    }
}

Decode

  • 存在 "@type" 注入安全问题, 不建议
java 复制代码
public class JetCacheFastjson2ValueDecoder extends Fastjson2ValueDecoder {

    public static final JetCacheFastjson2ValueDecoder INSTANCE = new JetCacheFastjson2ValueDecoder(true);

    public JetCacheFastjson2ValueDecoder(boolean useIdentityNumber) {
        super(useIdentityNumber);
    }

    @Override
    protected Object parseObject(byte[] buffer, int index, int len, Class clazz) {
        String s = new String(buffer, index, len, StandardCharsets.UTF_8);
        return JSON.parseObject(s, clazz, JSONReader.Feature.SupportAutoType);
    }
}

3. 自定义指定具体泛型 Decode 解码器 (推荐) 改动较少

  • 例如我们定义一个 List 泛型解码器
  • 问题就是需要让decode 知道具体的list 泛型类型
  • 通过显性传参指定泛型类型

还是原来的 Encode, 只是不需要输出指定类型了

  • 不指定classs类型
java 复制代码
public class JetCacheFastjson2ValueEncoder extends Fastjson2ValueEncoder {

    public JetCacheFastjson2ValueEncoder(boolean useIdentityNumber) {
        super(useIdentityNumber);
    }

    @Override
    public byte[] apply(Object value) {
        return super.apply(value);
    }

    @Override
    protected byte[] encodeSingleValue(Object value) {
        return JSON.toJSONString(value).getBytes();
    }
}
3.1 新建一个集合Json处理器 JetCacheFastjson2CollecationValueDecoder.java
java 复制代码
public class JetCacheFastjson2CollecationValueDecoder<T> extends AbstractJsonDecoder{

    private  final Class<T> collectionClassz;

    public JetCacheFastjson2CollecationValueDecoder(Class<T> classz) {
        super(true);
        this.collectionClassz = classz;
    }

    @Override
    protected Object parseObject(byte[] buffer, int index, int len, Class clazz) {
        String s = new String(buffer, index, len, StandardCharsets.UTF_8);
        if (Collection.class.isAssignableFrom(clazz)) {
            return JSON.parseArray(s, collectionClassz);
        }
        return JSON.parseObject(s, clazz);
    }

    @Override
    public Object apply(byte[] buffer) {
        try {
            return this.doApply(buffer);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
3.2 如果是List缓存或者其他的泛型缓存, 自定义定制解码器, 增强实现
  • ActivitySkRuleCacheBean 缓存配置对象
java 复制代码
@Configuration
public class ActivitySkRuleCacheBean {

    @Autowired
    private CacheManager cacheManager;

    // 活动id
    private Cache<Long, List<SkRuleDTO>> skRuleCache;

    @PostConstruct
    public void init() {
        //自动刷新 对一些key比较少,实时性要求不高,加载开销非常大的缓存场景,适合使用自动刷新。
        RefreshPolicy policy = RefreshPolicy.newPolicy(200, TimeUnit.MINUTES)//指定1小时刷新一次
                .stopRefreshAfterLastAccess(DAY_MINUTE, TimeUnit.MINUTES);//1天如果没有访问就停止刷新  指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新
        QuickConfig qc = QuickConfig.newBuilder(ACTIVITY_SK_RULE)
                .expire(Duration.ofMinutes(DAY_MINUTE))//过期时间为1天
                .cacheType(CacheType.REMOTE)
                //本地缓存更新后,将在所有的节点中删除缓存,以保持强一致性
                .syncLocal(true)//invalidate local cache in all jvm process after update(更新后使所有JVM进程中的本地缓存失效)
                .refreshPolicy(policy)
                .valueDecoder(((bytes)-> new JetCacheFastjson2CollecationValueDecoder<>(SkRuleDTO.class).apply(bytes)))
                .penetrationProtect(true)//当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。
                .build();
        skRuleCache = cacheManager.getOrCreateCache(qc);
    }

    @Bean
    public Cache<Long, List<SkRuleDTO>> getSkRuleCache() {
        return skRuleCache;
    }
}

重点

valueDecoder(((bytes)-> new JetCacheFastjson2CollecationValueDecoder<>(SkRuleDTO.class).apply(bytes)));

为当前缓存List泛型指定具体类型

4. 效果

成功变成了具体对象类型, 并且, 不存在 @type 安全问题

相关推荐
白山编程大哥9 小时前
Java 后端如何优雅地把 List 集合返回给前端
java·前端·list
MetaLite15 小时前
Spring 同一个类里调用 @CacheEvict,为什么缓存没清掉?
java·spring·缓存
hweiyu0015 小时前
Redis命令:SMOVE
redis·缓存
可乐ea15 小时前
Agent 隔离为什么失效:CPU 缓存成了跨机器信道
缓存·ai智能体·红队测试·沙箱安全·agent隔离·侧信道
谢白羽17 小时前
从传统运维到 AI Infra: K8s 上调度 100 张 GPU踩坑笔记
运维·人工智能·缓存·kubernetes·vllm·sglang·vllm-omni
萧瑟余晖18 小时前
Spring缓存抽象详解
java·spring·缓存
李纲明18 小时前
WordPress 前台改完还是旧页:浏览器、CDN、页面缓存、对象缓存怎么分层清
缓存·cdn·wordpress·devdiy
乐观的Terry20 小时前
AI翻译助手-JWT与redis无状态认证
数据库·redis·缓存
牧瀬クリスだ21 小时前
Redis String深度解析:从底层到五大实战场景
数据库·redis·缓存
鬼手点金21 小时前
缓存作为架构约束
开发语言·前端·人工智能·python·microsoft·缓存·架构