如何在Spring框架外反序列化Redis中存储的Java对象
解决Spring Session Redis二进制数据反序列化问题
你遇到的StreamCorruptedException核心原因确实是把Redis中存储的JDK序列化二进制数据当成UTF-8字符串处理,导致字节丢失或损坏。从你给出的Redis字符串开头\xAC\xED\x00\x05就能看出来,这是JDK序列化的标志性魔数,这类二进制数据根本不能直接转成String再转回byte[]——UTF-8是字符编码,不是二进制容器,很多无效的字节序列会被替换成�(也就是错误信息里的EFBFBDEF,这是UTF-8替换字符的字节码),直接导致反序列化失败。
无需修改序列化逻辑的解决方案
你完全不需要改动Spring端的序列化配置,只需要在Jedis操作时跳过String转换,直接读取原始二进制数据即可。Jedis提供了完整的二进制API,对应字符串操作的二进制版本,比如hGetAll(byte[] key)代替hgetAll(String key),这样就能拿到完整的序列化字节数组,直接反序列化就没问题了。
修改后的代码示例
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; import java.util.Set; import redis.clients.jedis.Jedis; private static void scanKeys(Jedis jedis, String prefix, String hashKey) throws IOException, ClassNotFoundException { // 将前缀转换为字节数组(Redis默认用UTF-8编码键) byte[] prefixBytes = prefix.getBytes(StandardCharsets.UTF_8); byte[] patternBytes = Arrays.copyOf(prefixBytes, prefixBytes.length + 1); patternBytes[prefixBytes.length] = '*'; // 拼接通配符 Set<byte[]> keys = jedis.keys(patternBytes); for (byte[] key : keys) { String keyStr = new String(key, StandardCharsets.UTF_8); if (!keyStr.contains("expir")) { // 使用二进制API获取哈希表的所有键值对 Map<byte[], byte[]> sessionMap = jedis.hGetAll(key); byte[] targetHashKeyBytes = hashKey.getBytes(StandardCharsets.UTF_8); for (Map.Entry<byte[], byte[]> entry : sessionMap.entrySet()) { if (Arrays.equals(entry.getKey(), targetHashKeyBytes) && entry.getValue() != null) { String hashKeyStr = new String(entry.getKey(), StandardCharsets.UTF_8); System.out.println(hashKeyStr); // 直接使用原始字节数组反序列化 try { Long id = (Long) deserialize(entry.getValue()); System.out.println(id); } catch (EOFException e) { e.printStackTrace(); break; } } } } } } public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException { // 使用try-with-resources自动关闭流 try (ByteArrayInputStream in = new ByteArrayInputStream(data); ObjectInputStream is = new ObjectInputStream(in)) { return is.readObject(); } }
关键改动说明
- 所有Redis键相关操作改用二进制API:
keys(byte[])、hGetAll(byte[]),彻底避免字符串转换带来的字节损坏 - 仅在需要打印或判断键的字符串内容时,才将字节数组转为String(确保用UTF-8编码,和Redis的键编码一致)
- 直接使用从Redis获取的原始
byte[]进行反序列化,完整保留JDK序列化的原始二进制数据
这样就能在不修改Spring Session序列化配置的前提下,正确反序列化出Java对象了。
内容的提问来源于stack exchange,提问作者Yen Wu
相关产品推荐
相关产品推荐

