欢迎光临Fuwu360     登录 | 免费注册 |
按CTRL+D收藏,下次访问更方便
当前位置: 首页 > 编程 > Spring boot使用Redis作为缓存的配置方法

浏览历史

Spring boot使用Redis作为缓存的配置方法

龙永超 / 2020-04-11

项目使用的Spring boot版本是1.5.9,Redis是5.0版本,Jdk1.8

项目使用的Spring boot版本是1.5.9,Redis是5.0版本,Jdk1.8

先在application.yml里面配置:

spring:
#缓存
cache:
type: NONE

注意实体类都必须可序列化,也就是implements Serializable

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。
Spring Data JPA为我们提供了下面的Serializer:

GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。

序列化方式对比: JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。

优点是反序列化时不需要提供类型信息(class),

但缺点是需要实现Serializable接口,还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。

Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。

优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。

但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。

通过查看源代码,发现其只在反序列化过程中用到了类型信息。

用Spring data redis做redis缓存的时候,指定RedisTemplate的key和value序列化时遇到的问题。

1.RedisTemplate的key指定成StringRedisSerializer序列化会报类型转换错误,如XXX类不能转换成String。

2.使用Jackson2JsonRedisSerializer序列化的时候,如果实体类上没有set方法反序列化会报错。

问题分析

问题1:使用StringRedisSerializer做key的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,在使用@Cacheable注解是key属性就只能传String进来。把这个序列化方式重写了,将泛型改成Object。源码:

import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.util.Assert;

import java.nio.charset.Charset;

/**
* 使用StringRedisSerializer做key的序列化时,StringRedisSerializer的泛型指定的是String,传其他对象就会报类型转换错误,
* 在使用@Cacheable注解是key属性就只能传String进来。把这个序列化方式重写了,将泛型改成Object。
*/
public class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;

private final String target = "\"";

private final String replacement = "";

public StringRedisSerializer() {
this(Charset.forName("UTF8"));
}

public StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}

@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}

@Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (null == string) {
return null;
}
string = string.replace(target, replacement);
return string.getBytes(charset);
}
}

问题2:我们放弃用jackjson来做value的序列化,使用FastJson来做。重写一些序列化器,并实现RedisSerializer接口。源码如下:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
* 我们放弃用jackjson来做value的序列化,使用FastJson来做。重写一些序列化器,并实现RedisSerializer接口
* @param <T>
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

private Class<T> clazz;

public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}

@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
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 (T) JSON.parseObject(str, clazz);

}
}

新加入一种序列化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;


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;
}


}


Java配置代码如下:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.core.RedisTemplate;
import org.springframework.cache.interceptor.KeyGenerator;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
* 2. * Redis配置
* 3. *
* 4. * @author fengshuonan
* 5. * @date 2018年1月28日 11:49:12
* 6.
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);

/**
* 采用RedisCacheManager作为缓存管理器
*
* @param redisTemplate
* @return SpringBoot项目启动时就会去找自定义配置的CacheManager对象并且自动应用到项目中
*/
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
logger.debug("生成RedisCacheManager缓存管理器...");
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
//默认超时时间,单位秒
redisCacheManager.setDefaultExpiration(7200);
//缓存超时时间Map,key为cacheName,value为超时,单位是秒
Map<String, Long> expiresMap = new HashMap<>();
// 缓存用户信息的cacheName和超时时间
expiresMap.put("user", 1800L);
//缓存default的cacheName和超时时间
expiresMap.put("default", 600L);
redisCacheManager.setExpires(expiresMap);
// 最好是带上前缀,否则id重复的话会有问题
redisCacheManager.setUsePrefix(true);
return redisCacheManager;
}

/**
* key的生成方式
*
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
logger.info("RedisCacheConfig.keyGenerator()");
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
// This will generate a unique key of the class name, the method name
//and all method parameters appended.
/* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");*/
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getSimpleName())
.append(":")
.append(method.getName())
.append(":");
for (Object obj : objects) {
if (null != obj) {
// 替换字符串
String objKey = JSON.toJSONString(obj);
objKey = objKey.replace(":", "=");
sb.append(objKey);
}
}
logger.debug("keyGenerator=" + sb.toString());
return sb.toString();
}
};
}


@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
ParserConfig.getGlobalInstance().addAccept("com.shangshiwendao.web.");
// 设置值(value)的序列化采用FastJsonRedisSerializer
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.afterPropertiesSet();
return redisTemplate;
}

}





微信或浏览器扫码阅读
用户评论 (共0条评论)

暂时还没有任何用户评论

总计 0 个记录,共 1 页。
亲,赐个留言吧!
captcha