Redis序列化配置类
Redis序列化配置类,用于定制Redis数据的存储和读取方式
package com.example.usermanagement.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis配置类* 配置Redis序列化方式,使存储的数据更易读*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 方案1:使用GenericJackson2JsonRedisSerializer(推荐)GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =new GenericJackson2JsonRedisSerializer();// String序列化StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key使用String序列化template.setKeySerializer(stringRedisSerializer);// hash的key也使用String序列化template.setHashKeySerializer(stringRedisSerializer);// value使用Jackson序列化template.setValueSerializer(genericJackson2JsonRedisSerializer);// hash的value使用Jackson序列化template.setHashValueSerializer(genericJackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}}
默认序列化的问题
Spring Boot默认使用JDK序列化,会产生以下问题:
// 默认JDK序列化存储的数据(不可读)
Key: "\xac\xed\x00\x05t\x00\x04user"
Value: "\xac\xed\x00\x05sr\x00\x1ccom.example.User..."// 自定义配置后的存储(可读)
Key: "user:1"
Value: {"id":1,"username":"admin","email":"admin@example.com"}
对比效果:
- JDK序列化:二进制格式,不可读,占用空间大
- JSON序列化:文本格式,可读性强,便于调试
2.1 RedisTemplate配置
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
作用:
- 创建自定义的
RedisTemplate
Bean <String, Object>
:Key为String类型,Value可以是任意对象factory
:Spring自动注入的Redis连接工厂
2.2 设置连接工厂
template.setConnectionFactory(factory);
- 建立RedisTemplate与Redis服务器的连接
factory
来自Spring Boot的自动配置
3. 序列化器详解
3.1 GenericJackson2JsonRedisSerializer
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
特点:
- 使用Jackson库进行JSON序列化
- 包含类型信息:存储时会保存对象的类名
- 反序列化时能正确还原为原始对象类型
- 支持复杂对象和集合
存储示例:
{"@class": "com.example.usermanagement.entity.User","id": 1,"username": "admin","email": "admin@example.com","status": 1
}
3.2 StringRedisSerializer
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
特点:
- 将字符串按UTF-8编码序列化
- 用于Key的序列化,保证Key的可读性
- 性能高,占用空间小
4. 四种序列化配置
4.1 Key序列化
template.setKeySerializer(stringRedisSerializer);
- 用途:普通的Redis Key
- 效果:
"user:1"
而不是二进制数据
4.2 Hash Key序列化
template.setHashKeySerializer(stringRedisSerializer);
- 用途:Hash数据结构中的field名
- 示例:
// 使用Hash存储用户信息
redisTemplate.opsForHash().put("user:1", "username", "admin");
redisTemplate.opsForHash().put("user:1", "email", "admin@example.com");
4.3 Value序列化
template.setValueSerializer(genericJackson2JsonRedisSerializer);
- 用途:普通的Redis Value
- 效果:对象被序列化为JSON格式
4.4 Hash Value序列化
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
- 用途:Hash数据结构中的值
- 效果:Hash中的每个值都按JSON格式存储
5. afterPropertiesSet()方法
template.afterPropertiesSet();
- 作用:确保所有配置生效
- 时机:在所有属性设置完成后调用
- 必需性:必须调用,否则配置可能不生效
6. 实际应用效果对比
6.1 存储User对象
Java代码:
@Autowired
private RedisTemplate<String, Object> redisTemplate;public void saveUser(User user) {redisTemplate.opsForValue().set("user:" + user.getId(), user);
}
Redis中的存储效果:
Key: user:1
Value:
{"@class": "com.example.usermanagement.entity.User","id": 1,"username": "admin","password": "123456","email": "admin@example.com","phone": "13800138001","status": 1,"score": 95,"createTime": ["java.util.Date", 1691823600000],"updateTime": ["java.util.Date", 1691823600000]
}
6.2 存储列表数据
public void saveUserList(List<User> users) {redisTemplate.opsForValue().set("users:all", users);
}
存储效果:
{"@class": "java.util.ArrayList","content": [{"@class": "com.example.usermanagement.entity.User","id": 1,"username": "admin"},{"@class": "com.example.usermanagement.entity.User", "id": 2,"username": "user001"}]
}
7. 高级配置选项
7.1 自定义ObjectMapper
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 自定义ObjectMapperObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(objectMapper);// 设置序列化器StringRedisSerializer stringSerializer = new StringRedisSerializer();template.setKeySerializer(stringSerializer);template.setHashKeySerializer(stringSerializer);template.setValueSerializer(serializer);template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;
}
7.2 各个配置项说明
setVisibility:
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- 设置Jackson的字段可见性
PropertyAccessor.ALL
:包括字段、getter、setter等JsonAutoDetect.Visibility.ANY
:任何访问级别都可见
activateDefaultTyping:
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL
);
- 启用类型信息存储
LaissezFaireSubTypeValidator
:宽松的类型验证器NON_FINAL
:对非final类启用类型信息
9.1 Service层使用
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String USER_KEY_PREFIX = "user:";public User getUserById(Long id) {String key = USER_KEY_PREFIX + id;// 从Redis获取(自动反序列化为User对象)User user = (User) redisTemplate.opsForValue().get(key);if (user != null) {return user;}// 从数据库查询user = userMapper.selectById(id);if (user != null) {// 存入Redis(自动序列化)redisTemplate.opsForValue().set(key, user, 2, TimeUnit.HOURS);}return user;}public void deleteUserCache(Long id) {String key = USER_KEY_PREFIX + id;redisTemplate.delete(key);}
}
9.2 Hash操作示例
public void saveUserToHash(User user) {String key = "user:hash:" + user.getId();// 将User对象的各个字段存储到Hash中redisTemplate.opsForHash().put(key, "username", user.getUsername());redisTemplate.opsForHash().put(key, "email", user.getEmail());redisTemplate.opsForHash().put(key, "user", user); // 整个对象
}
10. 常见问题与解决
10.1 类型转换异常
问题: ClassCastException: LinkedHashMap cannot be cast to User
原因: 反序列化时丢失了类型信息
解决:
// 方法1:使用强类型转换
User user = objectMapper.convertValue(redisTemplate.opsForValue().get(key), User.class
);// 方法2:确保使用GenericJackson2JsonRedisSerializer
10.2 日期格式问题
问题: 日期序列化格式不正确
解决:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
10.3 性能优化
// 连接池配置
@Bean
public LettuceConnectionFactory redisConnectionFactory() {GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();poolConfig.setMaxTotal(20);poolConfig.setMaxIdle(10);poolConfig.setMinIdle(5);LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379), clientConfig);
}