alibaba / jetcache

JetCache is a Java cache framework.
Apache License 2.0
4.96k stars 1.03k forks source link

CacheValueHolder类为何要使用final修饰?使用jackson某些情况下可能无法序列化和反序列化 #897

Closed Vastfy closed 3 weeks ago

Vastfy commented 3 weeks ago

版本:2.7.6 问题描述:使用自定义的jackson实现ValueEncoder和ValueDecoder如下: image 将上述两个类作为spring bean管理,并通过以下方式使用: image JacksonSerializer 类实现如下:

static class JacksonSerializer  {

        private final ObjectMapper objectMapper;

        public JacksonSerializer() {
            this((String) null);
        }

        public JacksonSerializer(ObjectMapper mapper) {
            Assert.notNull(mapper, "ObjectMapper must not be null!");
            this.objectMapper = mapper;
        }

        public JacksonSerializer(String classPropertyTypeName) {
            this(new ObjectMapper());
            registerNullValueSerializer(this.objectMapper, classPropertyTypeName);
            PolymorphicTypeValidator polymorphicTypeValidator = objectMapper.getPolymorphicTypeValidator();
            if (StringUtils.hasText(classPropertyTypeName)) {
//                this.objectMapper.activateDefaultTypingAsProperty(polymorphicTypeValidator, ObjectMapper.DefaultTyping.EVERYTHING, classPropertyTypeName);
                this.objectMapper.activateDefaultTypingAsProperty(polymorphicTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, classPropertyTypeName);
            } else {
//                this.objectMapper.activateDefaultTyping(polymorphicTypeValidator, ObjectMapper.DefaultTyping.EVERYTHING, JsonTypeInfo.As.PROPERTY);
// 核心在这里:当使用NON_FINAL时,由于缓存消息都会被CacheValueHolder封装并序列化为json字符串,此时不会包含CacheValueHolder的@class信息
                this.objectMapper.activateDefaultTyping(polymorphicTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            }
        }

        private static void registerNullValueSerializer(ObjectMapper objectMapper, @Nullable String classPropertyTypeName) {
            objectMapper.registerModule((new SimpleModule()).addSerializer(new NullValueSerializer(classPropertyTypeName)));
        }

        public byte[] serialize(@Nullable Object source) {
            if (source == null) {
                return new byte[0];
            } else {
                try {
                    return this.objectMapper.writeValueAsBytes(source);
                } catch (JsonProcessingException var3) {
                    throw new IllegalArgumentException("Could not write JSON: " + var3.getMessage(), var3);
                }
            }
        }

        public Object deserialize(@Nullable byte[] source) {
            return this.deserialize(source, Object.class);
        }

        @Nullable
        private  <T> T deserialize(@Nullable byte[] source, Class<T> type) {
            Assert.notNull(type, "Deserialization type must not be null! Please provide Object.class to make use of Jackson2 default typing.");
            if (Objects.isNull(source) || source.length <= 0) {
                return null;
            } else {
                try {
                    return this.objectMapper.readValue(source, type);
                } catch (Exception var4) {
                    throw new IllegalArgumentException("Could not read JSON: " + var4.getMessage(), var4);
                }
            }
        }

        private static class NullValueSerializer extends StdSerializer<NullValue> {
            private final String classIdentifier;

            NullValueSerializer(@Nullable String classIdentifier) {
                super(NullValue.class);
                this.classIdentifier = StringUtils.hasText(classIdentifier) ? classIdentifier : "@class";
            }

            @Override
            public void serialize(NullValue value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
                jsonGenerator.writeStartObject();
                jsonGenerator.writeStringField(this.classIdentifier, NullValue.class.getName());
                jsonGenerator.writeEndObject();
            }
        }

    }

以上是序列化器完整实现。 核心在这里:当使用NON_FINAL时,由于缓存消息都会被CacheValueHolder封装并序列化为json字符串,此时不会包含CacheValueHolder的"@class"信息,如下: image

当查询缓存时,会提示反序列化异常,如下: image

除非改成EVERYTHING,想请教下为啥要定义成final

areyouok commented 3 weeks ago

应该没有为什么,既然你在改造,自己先把它去掉好了

zzuhkp commented 1 day ago

@Vastfy 可以尝试使用 ObjectMapper.DefaultTyping.EVERYTHING,示例如下:

objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.EVERYTHING, JsonTypeInfo.As.PROPERTY);
image