SpringBoot2集成RedisCache
Posted 疯狂的妞妞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot2集成RedisCache相关的知识,希望对你有一定的参考价值。
应用层的东西,找到接口实现它即可。
如果想要自己选择序列化工具,难点还是在自动转型上,在字符串转成对象的过程中,Spring并未提供有效的、带Class参数的接口,类型自动转换问题,需要第三方框架自行处理。
最好选用带自动转型的序列化框架,错误的写法,很容易导致类型强转失败,本文采用的是FastJSON。
简单的工具类,按自己需求封装,可以指定各种数据类型序列化格式。
/** * @author ChenSS * @date 2018年7月13日 v1 * 2019年10月16日 v2 优化日期 */ public class FastJsonUtils { public static final SerializeConfig serializeConfig; static { serializeConfig = new SerializeConfig(); FastJsonDateSerializer dateTimeSerializer = new FastJsonDateSerializer("yyyy-MM-dd HH:mm:ss"); serializeConfig.put(Date.class, dateTimeSerializer); serializeConfig.put(java.sql.Timestamp.class, dateTimeSerializer); serializeConfig.put(java.sql.Date.class, new FastJsonDateSerializer("yyyy-MM-dd")); serializeConfig.put(java.sql.Time.class, new FastJsonDateSerializer("HH:mm:ss")); // // 使用和json-lib兼容的日期输出格式 // config.put(java.util.Date.class, new JSONLibDataFormatSerializer()); // config.put(java.sql.Date.class, new JSONLibDataFormatSerializer()); } }
FastJson2JsonRedisSerializer
很多人写这个类,完全模仿Jackson的写法,其实没必要,明确自己的需求,保留最少的代码即可。
我泛型直接写Object,因为代码已经能够处理全部类型的数据了。
import cn.seaboot.common.core.FastJsonUtils; 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; /** * @author Mr.css * @date 2020/1/2 11:24 */ public class FastJson2JsonRedisSerializer implements RedisSerializer<Object> { @Override public byte[] serialize(Object o) throws SerializationException { if (o == null) { return new byte[0]; } else { return JSON.toJSONString(o, FastJsonUtils.serializeConfig, SerializerFeature.WriteClassName).getBytes(Charset.defaultCharset()); } } @Override public Object deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length <= 0) { return null; } else { return JSON.parse(new String(bytes, Charset.defaultCharset())); } } }
RedisConfig
如果在使用Cache注解的时候有写key的习惯,KeyGenerator 可以不需要配置,我这里把函数名和所有的参数拼在一起,做成默认的Key值。
import cn.seaboot.common.core.Converter; import com.alibaba.fastjson.parser.ParserConfig; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.serializer.RedisSerializationContext; import javax.annotation.Resource; import java.time.Duration; /** * @author Mr.css on 2019/12/26 * @date 2019/12/31 */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Resource private LettuceConnectionFactory lettuceConnectionFactory; /** * Cache注解可以不指定key,需要有默认策略,按需调整 */ @Bean @Override public KeyGenerator keyGenerator() { return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb.append(method.getName()); if(params.length > 0){ for (int i = 1; i < params.length; i++) { sb.append(Converter.toString(params[i])); } } return sb.toString(); }; } @Bean @Override public CacheManager cacheManager() { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory); FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(); RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer); RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); //设置过期时间 30天 defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofDays(30)); //初始化RedisCacheManager RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig); //反序列化白名单 ParserConfig.getGlobalInstance().addAccept("cn.seaboot.admin.bean."); return cacheManager; } }
yml
redis: host: 127.0.0.1 port: 6379 timeout: 1000 jedis: pool: min-idle: 1 max-idle: 8 max-wait: 5000
Maven
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
补充
集成RedisCache,上面代码已经足够,这里介绍FastJSON的一些问题。
FastJSON自动转型的写法
public static void main(String[] args) { ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity"); TUserInfoEntity entity = new TUserInfoEntity(); entity.setId("13123"); //使用 SerializerFeature.WriteClassName 可以让JSON自动转型 //不依赖 clazz 参数也达到 JSON.parseObject(String json, Class<T> clazz) 相同效果 String str = JSON.toJSONString(entity, SerializerFeature.WriteClassName); System.out.println(JSON.parse(str).getClass()); }
ParserConfig.getGlobalInstance()的必要性
FastJSON最初是没有白名单这个要求的,addAccept接口的设计源自于系统漏洞。
假设去除掉白名单的设计,在知道全类名的情况下,通过Http接口即可创建出系统的任何对象。
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; /** * @author Mr.css * @date 2020/1/6 */ public class Test { public static void main(String[] args) { //在没有 ParserConfig.getGlobalInstance() 的情况下,只要知道全类名,即可 new 出程序中任何一个对象 ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity"); //下列这行代码,等效于Class.forName().newInstance() //也就是说,在使用FastJSON的系统中,但凡用到@ResponseBody的接口,都可以通过外部接口利用此bug System.out.println(JSON.parse("{"@type":"cn.swsk.xbry.entity.TUserInfoEntity"}").getClass()); } }
以上是关于SpringBoot2集成RedisCache的主要内容,如果未能解决你的问题,请参考以下文章