2026-06-24 f4bd1f3c89d906131495a0aca5aaf82966378510
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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 配置类
 */
@AutoConfiguration(before = RedissonAutoConfigurationV4.class) // 目的:使用自己定义的 RedisTemplate Bean
public class YudaoRedisAutoConfiguration {
 
    /**
     * 创建 RedisTemplate Bean,使用 JSON 序列化方式
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建 RedisTemplate 对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。
        template.setConnectionFactory(factory);
        // 使用 String 序列化方式,序列化 KEY 。
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 使用 JSON 序列化方式,序列化 VALUE
        RedisSerializer<?> redisSerializer = buildRedisSerializer();
        template.setValueSerializer(redisSerializer);
        template.setHashValueSerializer(redisSerializer);
        return template;
    }
 
    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);
        }
    }
 
}