diff --git a/src/main/java/com/njcn/middle/cache/support/CacheValueCodec.java b/src/main/java/com/njcn/middle/cache/support/CacheValueCodec.java new file mode 100644 index 0000000..5807c6f --- /dev/null +++ b/src/main/java/com/njcn/middle/cache/support/CacheValueCodec.java @@ -0,0 +1,37 @@ +package com.njcn.middle.cache.support; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.njcn.middle.cache.constant.CacheConstant; + +/** + * 缓存值编解码:对象 ↔ 字符串(fastjson),并承载空值标记。 + * + *

解码失败直接抛 fastjson 异常({@link RuntimeException}),由调用方决定显式暴露(public get) + * 还是当未命中降级(getOrLoad 内部)。 + */ +public class CacheValueCodec { + + /** 对象 → 字符串:null → 空值标记;否则 fastjson 序列化。 */ + public String encode(Object value) { + if (value == null) { + return CacheConstant.NULL_SENTINEL; + } + return JSON.toJSONString(value); + } + + /** 是否空值标记。 */ + public boolean isNullSentinel(String raw) { + return CacheConstant.NULL_SENTINEL.equals(raw); + } + + /** 字符串 → 对象(Class)。 */ + public T decode(String raw, Class type) { + return JSON.parseObject(raw, type); + } + + /** 字符串 → 对象(TypeReference,支持泛型)。 */ + public T decode(String raw, TypeReference type) { + return JSON.parseObject(raw, type); + } +} diff --git a/src/test/java/com/njcn/middle/cache/support/CacheValueCodecTest.java b/src/test/java/com/njcn/middle/cache/support/CacheValueCodecTest.java new file mode 100644 index 0000000..e37df46 --- /dev/null +++ b/src/test/java/com/njcn/middle/cache/support/CacheValueCodecTest.java @@ -0,0 +1,64 @@ +package com.njcn.middle.cache.support; + +import com.alibaba.fastjson.TypeReference; +import com.njcn.middle.cache.constant.CacheConstant; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CacheValueCodecTest { + + private final CacheValueCodec codec = new CacheValueCodec(); + + @Data + @AllArgsConstructor + @NoArgsConstructor + static class Foo { + private Long id; + private String name; + } + + @Test + void encodeNullReturnsSentinel() { + assertEquals(CacheConstant.NULL_SENTINEL, codec.encode(null)); + assertTrue(codec.isNullSentinel(codec.encode(null))); + assertFalse(codec.isNullSentinel("{\"id\":1}")); + } + + @Test + void encodeDecodePojoRoundTrip() { + Foo foo = new Foo(1L, "alice"); + String raw = codec.encode(foo); + Foo back = codec.decode(raw, Foo.class); + assertEquals(foo, back); + } + + @Test + void encodeDecodeStringRoundTrip() { + String raw = codec.encode("abc"); + assertEquals("abc", codec.decode(raw, String.class)); + } + + @Test + void decodeGenericWithTypeReference() { + List list = Arrays.asList(1, 2, 3); + String raw = codec.encode(list); + List back = codec.decode(raw, new TypeReference>() { + }); + assertEquals(list, back); + } + + @Test + void decodeDirtyStringThrows() { + assertThrows(RuntimeException.class, () -> codec.decode("{not-json", Foo.class)); + } +}