阿里FastJson2JsonRedisSerializer.java作为内部类强化RedisConfig的序列化实现
Posted 阿啄debugIT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里FastJson2JsonRedisSerializer.java作为内部类强化RedisConfig的序列化实现相关的知识,希望对你有一定的参考价值。
任何存储都需要序列化
任何存储都需要序列化。只不过常规你在用DB一类存储的时候,这个事情DB帮你在内部已经实现了(直接把SQL带有类型的数据转换成内部序列化的格式,存储;读取时再解析出来)。
而Redis并不会帮你做序列化这个事情。当你用Redis的key和value时,value对于redis来讲就是个byte array,需要把需要的数据结构转换成byte array,存储,等读取时再读出来。
- 若是字符串,因为字符串本来几乎就是byte array了,所以不需要自己处理;
- 若是boolean类型的true/false;你要自己定义redis里怎么表示true和false。比如你可以用1代表true,0代表false;也可以用“true”这个字符串代表true,“false”这个字符串代表false;
- 若是数字,可以直接存储数字的字符串表示(5 --> '5'),然后读取时再把数字字符串转回来(parseInt/parseDouble/...);
- 若是时间/日期,可以自己定义一种字符串表达,比如epoc timestamp这个数的字符串表示,又或者是ISO8601的格式。
- 若是复杂的数据结构,你需要自己用某种序列化格式来存,可以是json, protobuf, avro, java serialization, python pickle……
回到Spring这边,Spring的redisTemplate默认会使用java serialization做序列化。若用StringRedisTemplate,那么你set的所有数据都会被toString一下再存到redis里。但这个toString不一定能反解析的回来……
特别记住的序列化KryoRedisSerializer,速度很快!
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.io.ByteArrayOutputStream;
/**
* @param <T>
* @author 阿啄debugIT
*/
public class KryoRedisSerializer<T> implements RedisSerializer<T>
Logger logger = LoggerFactory.getLogger(KryoRedisSerializer.class);
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private static final ThreadLocal<Kryo> kryos = ThreadLocal.withInitial(Kryo::new);
private Class<T> clazz;
public KryoRedisSerializer(Class<T> clazz)
super();
this.clazz = clazz;
@Override
public byte[] serialize(T t) throws SerializationException
if (t == null)
return EMPTY_BYTE_ARRAY;
Kryo kryo = kryos.get();
kryo.setReferences(false);
kryo.register(clazz);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos))
kryo.writeClassAndObject(output, t);
output.flush();
return baos.toByteArray();
catch (Exception e)
logger.error(e.getMessage(), e);
return EMPTY_BYTE_ARRAY;
@Override
public T deserialize(byte[] bytes) throws SerializationException
if (bytes == null || bytes.length <= 0)
return null;
Kryo kryo = kryos.get();
kryo.setReferences(false);
kryo.register(clazz);
try (Input input = new Input(bytes))
return (T) kryo.readClassAndObject(input);
catch (Exception e)
logger.error(e.getMessage(), e);
return null;
总之简单一句话,你要形成一个序列化的约定,确保存进去的东西能解析回来不出错。
优秀讲解链接:
- https://blog.csdn.net/yuyeqianhen/article/details/90080532
- https://blog.csdn.net/moshowgame/article/details/83246363
阿里FastJson2JsonRedisSerializer.java作为内部类
用SpringBoot+Redis+SpringCache做个缓存,采用阿里FastJson2JsonRedisSerializer.java作为内部类,作为RedisConfig的强化序列化实现。
package AAAAAAAAAAA.common.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.time.Duration;
@Configuration
public class RedisConfig extends CachingConfigurerSupport
@Value("$spring.redis.host")
private String host;
@Value("$spring.redis.port")
private int port;
@Value("$spring.redis.password")
private String password;
@Value("$spring.redis.timeout")
private int timeout;
@Value("$spring.redis.jedis.pool.max-idle")
private int maxIdle;
@Value("$spring.redis.jedis.pool.max-wait")
private long maxWaitMillis;
@Bean
public JedisPool redisPoolFactory()
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
if (StringUtils.isNotBlank(password))
return new JedisPool(jedisPoolConfig, host, port, timeout, password);
else
return new JedisPool(jedisPoolConfig, host, port, timeout);
@Bean
JedisConnectionFactory jedisConnectionFactory()
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
jedisClientConfiguration.connectTimeout(Duration.ofMillis(timeout));
jedisClientConfiguration.usePooling();
return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration.build());
@Bean(name = "redisTemplate")
@SuppressWarnings("unchecked", "rawtypes")
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
RedisTemplate<Object, Object> template = new RedisTemplate<>();
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
ParserConfig.getGlobalInstance().addAccept("com.xiaolyuh.");
//使用 fastjson 序列化
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
// value 值的序列化采用 fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key 的序列化采用 StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
//缓存管理器
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory)
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory);
return builder.build();
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
@Bean
public KeyGenerator wiselyKeyGenerator()
return (target, method, params) ->
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params)
sb.append(obj.toString());
return sb.toString();
;
@Bean
public RedisTemplate<String, Serializable> limitRedisTemplate(RedisConnectionFactory redisConnectionFactory)
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
class FastJsonRedisSerializer<T> implements RedisSerializer<T>
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz)
super();
this.clazz = clazz;
@Override
public byte[] serialize(T t) throws SerializationException
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
@Override
public T deserialize(byte[] bytes) throws SerializationException
if (bytes==null||bytes.length <= 0)
return null;
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
代码解析
- 使用StringRedisSerializer做key的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,在使用@Cacheable注解是key属性就只能传String进来。把这个序列化方式重写了,将泛型改成Object。
- 使用FastJson来做value的序列化,重写一些序列化器,并实现RedisSerializer接口。
建议采用阿里FastJson的最新的依赖,避免一些以前版本存在远程代码执行高危安全漏洞
<!-- 1.2.41是最新的版本 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
友情链接:
- https://www.jianshu.com/p/23f2c4c92093?utm_source=oschina-app
- https://www.cnblogs.com/lic309/p/5056248.html
- https://blog.csdn.net/neosmith/article/details/46800235
以上是关于阿里FastJson2JsonRedisSerializer.java作为内部类强化RedisConfig的序列化实现的主要内容,如果未能解决你的问题,请参考以下文章