RedisTemplate的key默认序列化器问题

本文解决了一个在Spring Boot应用中使用Redis时遇到的奇怪问题:当在同一方法内先后调用HSet和HGet操作时一切正常,但在不同方法间执行同样的操作时却无法获取数据。通过对RedisTemplate序列化器的深入分析,最终找到了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis的客户端换成了spring-boot-starter-data-redis,碰到了一个奇怪的问题,

在同一个方法中

1.先hset,再hget,正常获得数据。

在不同的方法中 先hset,再hget获取不到数据,通过redis的monitor监控发现了命令的问题:


实际我的key为JK_HASH:csrk,hashkey为user,但是根据上图所示,实际执行的命令多了好多其他字符,这是什么原因呢?

在服务器端先确认发现实际有这个Hash,通过hset可以得到正确的数据,所以第一次执行hset的时候命令是正常的,问题可能出现在hget上面,先打开源码看一下

	@SuppressWarnings("unchecked")
	public HV get(K key, Object hashKey) {
		final byte[] rawKey = rawKey(key);
		final byte[] rawHashKey = rawHashKey(hashKey);

		byte[] rawHashValue = execute(new RedisCallback<byte[]>() {

			public byte[] doInRedis(RedisConnection connection) {
				return connection.hGet(rawKey, rawHashKey);
			}
		}, true);

		return (HV) deserializeHashValue(rawHashValue);
	}

从这里可以看到实际上传给redis的都是byte数据,而byte数组是rawKey和rawHashKey生成的,先看下rawKey方法

	@SuppressWarnings("unchecked")
	byte[] rawKey(Object key) {
		Assert.notNull(key, "non null key required");
		if (keySerializer() == null && key instanceof byte[]) {
			return (byte[]) key;
		}
		return keySerializer().serialize(key);
	}
然后进一步跟踪keySerializer()方法
	RedisSerializer keySerializer() {
		return template.getKeySerializer();
	}
	public RedisSerializer<?> getKeySerializer() {
		return keySerializer;
	}

最后跟踪到是RedisTemplate中的属性keySerializer导致的,而通过打印keySerializer的class发现 默认使用的是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,但它是如何进行初始化的呢,默认的构造函数中并没有对该属性进行初始化。

根据RedisTemplate的类关系发现它是继承RedisAccessor的,而此类是实现的org.springframework.beans.factory.InitializingBean接口,这个接口有个特性,凡是继承该接口的类,在初始化bean的时候会执行afterPropertiesSet方法。

而afterPropertiesSet方法中,确实对keySerializer进行了初始化:

public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}


if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}

在这里可以看到默认使用的正是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,而问题正在这里,通过查询可以发现序列化器有这些,而在这里我们需要使用的是StringRedisSerializer


加入如下代码:

	@Autowired(required = false)
	public void setRedisTemplate(RedisTemplate redisTemplate) {
	    RedisSerializer stringSerializer = new StringRedisSerializer();
	    redisTemplate.setKeySerializer(stringSerializer);
	    redisTemplate.setValueSerializer(stringSerializer);
	    redisTemplate.setHashKeySerializer(stringSerializer);
	    redisTemplate.setHashValueSerializer(stringSerializer);
	    this.redisTemplate = redisTemplate;
	}
重新进行测试,方法1hset,方法2hget,方法2能拿到正确的数据,完毕。


<think>嗯,用户想知道如何在Spring Boot 3.0.2中配置RedisTemplate实现JSON序列化。首先,我需要回忆一下RedisTemplate的基本配置方法。在Spring Boot中,默认RedisTemplate使用JdkSerializationRedisSerializer,这会导致存储的数据不易阅读,所以用户想换成JSON格式。 首先,可能需要添加相关的依赖。比如,Spring Boot Starter Data Redis,还有JSON处理库比如Jackson的依赖。不过,Spring Boot可能已经自带Jackson,所以是否需要显式引入呢?需要检查一下。 接下来,配置Redis连接,通常是在application.properties里设置host、port、password这些。然后,创建一个配置类,比如RedisConfig,用@Configuration注解,并在里面定义RedisConnectionFactory,可能还需要配置Lettuce或者Jedis的连接。不过Spring Boot 2.x之后默认用Lettuce,所以用户可能不需要改这个,除非有特殊需求。 然后,关键是如何配置RedisTemplate的序列化方式。需要创建RedisTemplate的Bean,并设置key和value的序列化器。对于JSON,可以使用GenericJackson2JsonRedisSerializer,或者自定义ObjectMapper。例如,可能先配置一个ObjectMapper,设置一些特性,比如禁用FAIL_ON_UNKNOWN_PROPERTIES,然后将其注入到RedisTemplate中。 另外,还要考虑hash key和hash value的序列化,通常使用String序列化器,比如StringRedisSerializer,这样键会是可读的字符串,而值则是JSON格式。 可能还需要在配置类中启用缓存相关的注解,比如@EnableCaching,但用户的问题可能不涉及缓存,而是直接使用RedisTemplate。 然后,用户可能需要一个例子,展示如何配置这些Bean,比如RedisTemplate<String, Object>,并设置序列化器。同时,测试一下是否正确存储和读取JSON数据。 还需要考虑是否要处理泛型类型,GenericJackson2JsonRedisSerializer会在JSON中添加类型信息,这样反序列化时不会有问题。而Jackson2JsonRedisSerializer可能需要明确的类型,但在配置时可能更灵活。 可能还需要参考Spring Boot的官方文档,确认在3.0.2版本中是否有变化。例如,自动配置类可能涉及RedisAutoConfiguration,而用户需要自定义覆盖默认RedisTemplate。 另外,用户提供的引用中有提到CacheAutoConfiguration,但用户的问题是关于RedisTemplate的,所以可能这部分引用不太相关。但另一个引用提到@KafkaListener,这和当前问题无关,但可能用户之前有相关的问题,需要忽略这部分。 最后,要确保代码示例正确,比如配置类、Bean的定义,以及使用@Autowired注入RedisTemplate,并在服务类中使用它来操作数据。测试的时候,保存一个对象到Redis,然后读取出来,确保序列化和反序列化正确。 可能还需要提到排除默认序列化器,或者处理潜在的序列化问题,比如日期格式、空值处理等。这些可以通过自定义ObjectMapper来调整。 总结步骤:添加依赖,配置连接信息,创建配置类,定义RedisTemplate Bean并设置JSON序列化器,测试验证。</think>在Spring Boot 3.0.2中配置`RedisTemplate`实现JSON序列化需通过以下步骤实现: --- ### 步骤1:添加依赖 在`pom.xml`中引入Spring Data Redis和Jackson依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` --- ### 步骤2:配置Redis连接 在`application.properties`中设置Redis连接信息: ```properties spring.data.redis.host=localhost spring.data.redis.port=6379 spring.data.redis.password= ``` --- ### 步骤3:自定义RedisTemplate 创建配置类`RedisConfig`,覆盖默认的序列化方式: ```java 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; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // 设置Key序列化为String template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // 设置Value序列化为JSON GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); template.setValueSerializer(jsonSerializer); template.setHashValueSerializer(jsonSerializer); template.afterPropertiesSet(); return template; } } ``` --- ### 步骤4:使用RedisTemplate操作数据 在Service类中注入并使用: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private RedisTemplate<String, Object> redisTemplate; public void saveUser(String key, User user) { redisTemplate.opsForValue().set(key, user); } public User getUser(String key) { return (User) redisTemplate.opsForValue().get(key); } } ``` --- ### 关键点说明 1. **序列化选择** - `GenericJackson2JsonRedisSerializer`会在JSON中添加类型元数据(如`@class`字段),确保反序列化时类型正确[^1]。 - 若需自定义JSON格式(如日期处理),可注入`ObjectMapper`实例并配置。 2. **性能优化** 通过`StringRedisSerializer`优化键的存储效率,避免二进制乱码问题[^2]。 --- ### 测试验证 存储对象后,Redis中会显示类似: ```json { "@class": "com.example.User", "id": 1, "name": "John" } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值