springboot2 - lettuce
spring 操作 redis,默认使用的是 lettuce,介绍一下相关代码。
Maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
</dependencies>
yml
# redis 缓存配置,注意调整 yml 配置的时候,还需要调整 maven 依赖
spring:
redis:
host: 127.0.0.1
port: 6379
timeout: 1000
lettuce:
pool:
min-idle: 1
max-idle: 8
max-wait: 5000
RedisTemplate
这是个工具类,用于手动操作 redis,二次封装的时候会用到。
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;
/**
* 使用 jdk 序列化的工具类(提供参考,未使用)
*
* @return RedisTemplate
*/
public RedisTemplate<String, Object> createJdkTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
CacheManager
做下列配置之后,@Cacheable、@CacheEvict 等注解,也会使用 redis。
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;
/**
* @see org.springframework.cache.annotation.Cacheable
*/
@Bean
@Override
public CacheManager cacheManager() {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);
FastJsonRedisSerializer serializer = new FastJsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair =
RedisSerializationContext.SerializationPair.fromSerializer(serializer);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
// 设置过期时间 30天,这个地方需要给 defaultCacheConfig 重新赋值
defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofDays(30));
// 初始化RedisCacheManager
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
return cacheManager;
}
}
fastjson2 实现 RedisSerializer
需要强调一下:AutoTypeBeforeHandler,它作用是声明一份白名单,处在白名单下的类,才允许进行反序列化。
见过不少代码,直接写了个 JSONReader.autoTypeFilter("com"),这是很不明智的。
反序列化过程,是分析 redis 中的数据,根据数据创建出对象实例。
如果程序没有限制,黑客可以伪造一份数据,让程序创建出不该创建的对象实例。
所以说, JSONReader.autoTypeFilter() 范围要尽量小。
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
/**
* Redis 使用 FastJson 序列化数据,满足所有数据类型的序列化
*
* @author Mr.css
* @version 2020-01-02 11:24
*/
public class FastJsonRedisSerializer implements RedisSerializer<Object> {
/**
* 包含各类序列化配置
*/
private final JSONWriter.Feature[] features;
/**
* 反序列化拦截器
*/
private final JSONReader.AutoTypeBeforeHandler filter;
/**
* 默认序列化的时候,写入全类名
*
* @param names 允许自动转型的包
*/
public FastJsonRedisSerializer(String... names) {
this.features = new JSONWriter.Feature[]{JSONWriter.Feature.WriteClassName};
this.filter = JSONReader.autoTypeFilter(names);
}
/**
* 序列化
*
* @param obj 对象实体
* @return 字节数组
* @throws SerializationException -
*/
@Override
public byte[] serialize(Object obj) throws SerializationException {
if (obj == null) {
return new byte[0];
} else {
return JSON.toJSONBytes(obj, features);
}
}
/**
* 反序列化
*
* 不在白名单的对象,不会反序列化失败,而是返回 com.alibaba.fastjson2.JSONObject。
*
* @param bytes 字节数组
* @return 对象实体
* @throws SerializationException -
*/
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
} else {
return JSON.parseObject(bytes, Object.class, filter);
}
}
}
测试函数
class Test{
public static void main(String[] args) {
Person person = new Person();
// 限定只能序列化 cn.seaboot 下的对象
FastJsonRedisSerializer serializer = new FastJsonRedisSerializer("cn.seaboot");
byte[] bytes = serializer.serialize(person);
System.out.println(serializer.deserialize(bytes).getClass());
}
}
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!