2026-06-30 24681c81c09022f584a57006f2534b5f74723414
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java
@@ -1,22 +1,11 @@
package cn.iocoder.yudao.framework.redis.config;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.hutool.core.util.StrUtil;
import org.redisson.spring.starter.RedissonAutoConfigurationV4;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;
/**
 * Redis 配置类
@@ -43,103 +32,12 @@
        return template;
    }
    @SuppressWarnings("UnnecessaryLocalVariable")
    public static RedisSerializer<?> buildRedisSerializer() {
        // 使用自定义序列化器,解决 Set/List 等 Collection 反序列化类型问题
        return new TypedJsonRedisSerializer();
    }
    /**
     * 自定义 Redis 序列化器,解决 Set/List 等 Collection 反序列化类型问题
     */
    private static class TypedJsonRedisSerializer implements RedisSerializer<Object> {
        private static final String JAVA_TYPE_PREFIX = "@class:";
        private static final ObjectMapper OBJECT_MAPPER = JsonUtils.getObjectMapper();
        private static final TypeReference<HashSet<Object>> HASH_SET_TYPE = new TypeReference<>() {};
        private static final TypeReference<LinkedHashSet<Object>> LINKED_HASH_SET_TYPE = new TypeReference<>() {};
        private static final TypeReference<TreeSet<Object>> TREE_SET_TYPE = new TypeReference<>() {};
        @Override
        public byte[] serialize(Object value) {
            if (value == null) {
                return new byte[0];
            }
            // 将类型信息与 JSON 一起存储
            String json = JsonUtils.toJsonString(value);
            String typePrefix = JAVA_TYPE_PREFIX + value.getClass().getName() + ":";
            return (typePrefix + json).getBytes(StandardCharsets.UTF_8);
        }
        @Override
        public Object deserialize(byte[] bytes) {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            String content = new String(bytes, StandardCharsets.UTF_8);
            if (!content.startsWith(JAVA_TYPE_PREFIX)) {
                // 兼容旧数据:没有类型信息的情况
                // 检测是否为 JSON 数组,若是则转换为 HashSet
                return deserializeLegacyData(content);
            }
            // 提取类型信息和 JSON
            int colonIndex = content.indexOf(':', JAVA_TYPE_PREFIX.length());
            if (colonIndex == -1) {
                return deserializeLegacyData(content);
            }
            String className = content.substring(JAVA_TYPE_PREFIX.length(), colonIndex);
            String json = content.substring(colonIndex + 1);
            try {
                Class<?> clazz = Class.forName(className);
                // 特殊处理 Set 类型,因为 Jackson 默认会将 JSON 数组反序列化为 List
                return deserializeWithTypeHint(json, clazz);
            } catch (ClassNotFoundException e) {
                // 类型不存在时,尝试兼容解析
                return deserializeLegacyData(json);
            } catch (Exception e) {
                // 解析失败时,尝试兼容解析
                return deserializeLegacyData(json);
            }
        }
        /**
         * 兼容旧数据(没有类型前缀)的解析
         * 对于 JSON 数组,默认转换为 HashSet
         */
        private Object deserializeLegacyData(String content) {
            if (StrUtil.isEmpty(content)) {
                return null;
            }
            content = content.trim();
            // 判断是否为 JSON 数组
            if (content.startsWith("[") && content.endsWith("]")) {
                try {
                    // 数组类型默认转为 HashSet
                    return OBJECT_MAPPER.readValue(content, HASH_SET_TYPE);
                } catch (Exception e) {
                    return JsonUtils.parseObject(content, Object.class);
                }
            }
            // 其他类型直接解析
            return JsonUtils.parseObject(content, Object.class);
        }
        private Object deserializeWithTypeHint(String json, Class<?> clazz) {
            // 处理 Set 接口的实现类
            if (HashSet.class.isAssignableFrom(clazz) ||
                "java.util.Collections$UnmodifiableSet".equals(clazz.getName()) ||
                "java.util.Collections$SingletonSet".equals(clazz.getName()) ||
                Set.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, HASH_SET_TYPE);
            }
            if (LinkedHashSet.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, LINKED_HASH_SET_TYPE);
            }
            if (TreeSet.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, TREE_SET_TYPE);
            }
            // 使用 Jackson 的 readValue 指定类型,确保 POJO 正确反序列化
            return OBJECT_MAPPER.readValue(json, clazz);
        }
        RedisSerializer<Object> json = RedisSerializer.json();
        // 特殊:spring boot 4.x 无需解决 LocalDateTime 的序列化
        // 原因:Spring Data Redis 4 使用 Jackson 3,RedisSerializer.json() 已支持 Java Time 类型
        return json;
    }
}