配置RedissonClient
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// 单节点模式
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://127.0.0.1:6379");
singleServerConfig.setPassword("");
// 使用json序列化方式
Codec codec = new JsonJacksonCodec();
config.setCodec(codec);
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
除上方配置的单机模式,其它模式配置:
数据序列化
Redisson的对象编码类是用于将对象进行序列化和反序列化,以实现对该对象在Redis里的读取和存储。Redisson提供了以下几种的对象编码应用,以供大家选择:
编码类名称 | 说明 |
org.redisson.codec.JsonJacksonCodec | Jackson JSON 编码 默认编码 |
org.redisson.codec.AvroJacksonCodec | Avro 一个二进制的JSON编码 |
org.redisson.codec.SmileJacksonCodec | Smile 另一个二进制的JSON编码 |
org.redisson.codec.CborJacksonCodec | CBOR 又一个二进制的JSON编码 |
org.redisson.codec.MsgPackJacksonCodec | MsgPack 再来一个二进制的JSON编码 |
org.redisson.codec.IonJacksonCodec | Amazon Ion 亚马逊的Ion编码,格式与JSON类似 |
org.redisson.codec.KryoCodec | Kryo 二进制对象序列化编码 |
org.redisson.codec.SerializationCodec | JDK序列化编码 |
org.redisson.codec.FstCodec | FST 10倍于JDK序列化性能而且100%兼容的编码 |
org.redisson.codec.LZ4Codec | LZ4 压缩型序列化对象编码 |
org.redisson.codec.SnappyCodec | Snappy 另一个压缩型序列化对象编码 |
org.redisson.client.codec.JsonJacksonMapCodec | 基于Jackson的映射类使用的编码。可用于避免序列化类的信息,以及用于解决使用byte[]遇到的问题。 |
org.redisson.client.codec.StringCodec | 纯字符串编码(无转换) |
org.redisson.client.codec.LongCodec | 纯整长型数字编码(无转换) |
org.redisson.client.codec.ByteArrayCodec | 字节数组编码 |
org.redisson.codec.CompositeCodec | 用来组合多种不同编码在一起 |
自定义序列化
需求背景:
项目之前使用的RestTemplate api,RestTemplate的序列化使用的RedisSerializer接口,使用的实现类Jackson2JsonRedisSerializer。
现在改为使用RedissonClient,RedissonClient的序列化使用的Codec接口,默认实现类JsonJacksonCodec。
2种实现类序列化方式不同,而生产环境数据迁移有风险,所以需实现Codec接口,自定义RedissonClient序列化方式,替换Redisson的序列化对象JsonJacksonCodec,兼容旧数据。
目的:
兼容原有RedisTemplate Jackson2JsonRedisSerializer对象序列化数据。
分析:
RedisTemplate初始化代码:
protected RedisTemplate<String, Object> buildRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
JsonJacksonCodec构造器初始化源码:
public JsonJacksonCodec() {
this(new ObjectMapper());
}
跟踪发现底层初始化时,默认给ObjectMapper设置了一些参数:
init(this.mapObjectMapper);
protected void init(ObjectMapper objectMapper) {
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.setVisibility(objectMapper.getSerializationConfig()
.getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.enable(Feature.WRITE_BIGDECIMAL_AS_PLAIN);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
objectMapper.addMixIn(Throwable.class, ThrowableMixIn.class);
}
就是这些ObjectMapper的初始化参数导致对象序列化数据不兼容,所以自定义Codec,和原来ObjectMapper的初始化保持一致即可!
实现:
自定义Codec实现类:RedissonCodec
/**
* 替换Redisson的序列化对象JsonJacksonCodec,兼容原有RedisTemplate的对象序列化。
*
* @author yangzihe
* @date 2022/12/13
*/
public class RedissonCodec extends BaseCodec {
protected final ObjectMapper mapObjectMapper;
/**
* @see JsonJacksonCodec
*/
private final Encoder encoder = new Encoder() {
@Override
public ByteBuf encode(Object in) throws IOException {
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
try {
ByteBufOutputStream os = new ByteBufOutputStream(out);
mapObjectMapper.writeValue((OutputStream) os, in);
return os.buffer();
} catch (IOException e) {
out.release();
throw e;
} catch (Exception e) {
out.release();
throw new IOException(e);
}
}
};
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
return mapObjectMapper.readValue((InputStream) new ByteBufInputStream(buf), Object.class);
}
};
public RedissonCodec(ObjectMapper mapObjectMapper) {
this(mapObjectMapper, true);
}
public RedissonCodec(ObjectMapper mapObjectMapper, boolean copy) {
if (copy) {
this.mapObjectMapper = mapObjectMapper.copy();
} else {
this.mapObjectMapper = mapObjectMapper;
}
}
@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}
@Override
public Encoder getValueEncoder() {
return encoder;
}
@Override
public ClassLoader getClassLoader() {
if (mapObjectMapper.getTypeFactory().getClassLoader() != null) {
return mapObjectMapper.getTypeFactory().getClassLoader();
}
return super.getClassLoader();
}
}
redis工具类
public final class RedisUtils {
public static final Codec REDISSON_CODEC;
static {
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
REDISSON_CODEC = new RedissonCodec(om);
}
private RedisUtils() {}
}
代码使用
redissonClient.getConfig().setCodec(RedisUtils.REDISSON_CODEC);
redissonClient.getSet(redisKey, RedisUtils.REDISSON_CODEC).add(object);
redissonClient.getSet(redisKey, RedisUtils.REDISSON_CODEC).remove(object);