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);
|
}
|
}
|
|
}
|