springboot2.x使用redis作為快取(使用fastjson序列化的方式,並除錯反序列化異常
阿新 • • 發佈:2019-02-14
1.redis是記憶體資料庫,可以單獨作為資料庫(有持久化方案),也可以作為快取(一般為MySQL搭配)
1.1 可以通過jedis,程式碼的方式手動將其傳入redis作為快取;
1.2 也可以通過註解的方式,和spring boot整合,通過@cacheable...的方式自動存入redis(本文的探討方式)
2.springboot2.x與1.x版本用法相差較大
例如:快取管理器的自定義方式
- @Bean //spring boot1.x版本的使用方式
- public CacheManager cacheManager(RedisTemplate redisTemplate)
{ - RedisCacheManager cacheManager= new RedisCacheManager(redisTemplate);
- cacheManager.setDefaultExpiration(60);
- Map<String,Long> expiresMap=new HashMap<>();
- expiresMap.put("Product",5L);
- cacheManager.setExpires(expiresMap);
- return cacheManager;
- }
到了2.x版本,沒有這個構造方法,該方式已經不能使用;
3.springboot2.x使用redis作為快取
思路:
1.編寫RedisConfig配置類----配置類裡面重構cacheManager和redisTemplate; 後期可以在該類中設定key的生成策略等...
2.實現物件的快取,定義自己的序列化和反序列化器。(必須定義自己的)
在redisConfig類中不要匯入:
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;//不要匯入
匯入:
import zmc.leon.mcd.Util.FastJsonRedisSerializer;//自定義的序列化反序列化器
3.spring boot+mybatis的controller、service、dao層的編寫(省略)
RedisConfig.java
- package zmc.leon.mcd.Config;
- //import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
- import com.alibaba.fastjson.parser.ParserConfig;
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.fasterxml.jackson.databind.SerializationFeature;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
- 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.context.annotation.Primary;
- 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.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.*;
- import zmc.leon.mcd.Util.FastJsonRedisSerializer;
- import javax.crypto.KeyGenerator;
- import java.lang.reflect.Method;
- import java.time.Duration;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
- @Configuration
- @EnableCaching
- public class RedisConfig extends CachingConfigurerSupport {
- /**
- * 重寫Redis序列化方式,使用Json方式:
- * 當我們的資料儲存到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到資料庫的。RedisTemplate預設使用的是JdkSerializationRedisSerializer,StringRedisTemplate預設使用的是StringRedisSerializer。
- * Spring Data JPA為我們提供了下面的Serializer:
- * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
- * 在此我們將自己配置RedisTemplate並定義Serializer。
- *
- */
- // 存入redis時,預設使用的是JdkSerializationRedisSerializer,使得存入的資料全部序列化了,所需自定義一個RedisTemplate,使用其他序列化方式
- //當redis依賴包匯入的時候,預設的cache即可自動變成redis模式;如果只是匯入cache的依賴,則預設的是simpleCacheManager;
- // 使用redis快取時,RedisCacheManager生成RedisCache後生成快取時預設使用JdkSerializationRedisSerializer序列化(cache儲存的時候)
- //當ioc容器內沒有自定義的快取管理器的時候---預設使用自帶的;
- //當通過@Bean在ioc容器中注入了以下管理器,則會使用自定義的管理器;
- // @Bean
- // public CacheManager cacheManager(RedisConnectionFactory factory) {
- // RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一個預設配置,通過config物件即可對快取進行自定義配置
- // config = config.entryTtl(Duration.ofMinutes(1)) // 設定快取的預設過期時間,也是使用Duration設定
- // .disableCachingNullValues(); // 不快取空值
- //
- // // 設定一個初始化的快取空間set集合
- // Set<String> cacheNames = new HashSet<>();
- // cacheNames.add("my-redis-cache1");
- // cacheNames.add("my-redis-cache2");
- //
- // // 對每個快取空間應用不同的配置
- // Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
- // configMap.put("my-redis-cache1", config);
- // configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
- //
- // RedisCacheManager cacheManager = RedisCacheManager.builder(factory) // 使用自定義的快取配置初始化一個cacheManager
- // .initialCacheNames(cacheNames) // 注意這兩句的呼叫順序,一定要先呼叫該方法設定初始化的快取名,再初始化相關的配置
- // .withInitialCacheConfigurations(configMap)
- // .build();
- // return cacheManager;
- // }
- @Bean
- @Primary//當有多個管理器的時候,必須使用該註解在一個管理器上註釋:表示該管理器為預設的管理器
- public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
- //初始化一個RedisCacheWriter
- RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
- //序列化方式1
- //設定CacheManager的值序列化方式為JdkSerializationRedisSerializer,但其實RedisCacheConfiguration預設就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下(4行)註釋程式碼為預設實現
- // ClassLoader loader = this.getClass().getClassLoader();
- // JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
- // RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
- // RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
- //序列化方式1---另一種實現方式
- //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//該語句相當於序列化方式1
- //序列化方式2
- FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);//JSONObject
- RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
- RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
- //序列化方式3
- //Jackson2JsonRedisSerializer serializer=new Jackson2JsonRedisSerializer(Object.class);
- //RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer);
- //RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
- defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(100));//設定過期時間
- // //設定預設超過期時間是30秒
- // defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
- //初始化RedisCacheManager
- RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
- //設定白名單---非常重要********
- /*
- 使用fastjson的時候:序列化時將class資訊寫入,反解析的時候,
- fastjson預設情況下會開啟autoType的檢查,相當於一個白名單檢查,
- 如果序列化資訊中的類路徑不在autoType中,
- 反解析就會報com.alibaba.fastjson.JSONException: autoType is not support的異常
- 可參考 https://blog.csdn.net/u012240455/article/details/80538540
- */
- ParserConfig.getGlobalInstance().addAccept("zmc.leon.mcd.entity.");
- return cacheManager;
- }
- /**
- * 設定 redis 資料預設過期時間
- * 設定@cacheable 序列化方式
- * @return
- */
- @Bean
- public RedisCacheConfiguration redisCacheConfiguration(){
- FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
- RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
- configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(30));
- return configuration;
- }
- @Bean(name = "redisTemplate")
- @SuppressWarnings("unchecked")
- @ConditionalOnMissingBean(name = "redisTemplate")
- public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- RedisTemplate<Object, Object> template = new RedisTemplate<>();
- //使用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 KeyGenerator KeyGenerator() {
- // return new KeyGenerator(){
- // public Object generate(Object target, Method method, Object... 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();
- // }
- // };
- // }
- }
FastJsonRediaSerializer.java
- package zmc.leon.mcd.Util;
- 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;
- /*
- 要實現物件的快取,定義自己的序列化和反序列化器。使用阿里的fastjson來實現的比較多。
- */
- public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
- private 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 (null == t) {
- return new byte[0];
- }
- return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
- }
- @Override
- public T deserialize(byte[] bytes) throws SerializationException {
- if (null == bytes || bytes.length <= 0) {
- return null;
- }
- String str = new String(bytes, DEFAULT_CHARSET);
- return (T) JSON.parseObject(str, clazz);
- }
- }
注意:
1.一定要匯入自己的util包
import zmc.leon.mcd.Util.FastJsonRedisSerializer;
2.快取管理器中一定要加入(白名單),其中的包名:為自己的bean類的所在包
ParserConfig.getGlobalInstance().addAccept("zmc.leon.mcd.entity.");
沒有的話會有以下異常:
com.alibaba.fastjson.JSONException: autoType is not support
具體解釋請關注: