1. 程式人生 > >springboot2.x使用redis作為快取(使用fastjson序列化的方式,並除錯反序列化異常

springboot2.x使用redis作為快取(使用fastjson序列化的方式,並除錯反序列化異常

1.redis是記憶體資料庫,可以單獨作為資料庫(有持久化方案),也可以作為快取(一般為MySQL搭配)

       1.1 可以通過jedis,程式碼的方式手動將其傳入redis作為快取;

       1.2 也可以通過註解的方式,和spring boot整合,通過@cacheable...的方式自動存入redis(本文的探討方式)

2.springboot2.x與1.x版本用法相差較大

     例如:快取管理器的自定義方式

  1. @Bean //spring boot1.x版本的使用方式
  2. public CacheManager cacheManager(RedisTemplate redisTemplate)
    {
  3. RedisCacheManager cacheManager= new RedisCacheManager(redisTemplate);
  4. cacheManager.setDefaultExpiration(60);
  5. Map<String,Long> expiresMap=new HashMap<>();
  6. expiresMap.put("Product",5L);
  7. cacheManager.setExpires(expiresMap);
  8. return cacheManager;
  9. }

   到了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

  1. package zmc.leon.mcd.Config;
  2. //import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
  3. import com.alibaba.fastjson.parser.ParserConfig;
  4. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  5. import com.fasterxml.jackson.annotation.PropertyAccessor;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7. import com.fasterxml.jackson.databind.SerializationFeature;
  8. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  9. import org.springframework.cache.CacheManager;
  10. import org.springframework.cache.annotation.CachingConfigurerSupport;
  11. import org.springframework.cache.annotation.EnableCaching;
  12. import org.springframework.context.annotation.Bean;
  13. import org.springframework.context.annotation.Configuration;
  14. import org.springframework.context.annotation.Primary;
  15. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  16. import org.springframework.data.redis.cache.RedisCacheManager;
  17. import org.springframework.data.redis.cache.RedisCacheWriter;
  18. import org.springframework.data.redis.connection.RedisConnectionFactory;
  19. import org.springframework.data.redis.core.RedisTemplate;
  20. import org.springframework.data.redis.serializer.*;
  21. import zmc.leon.mcd.Util.FastJsonRedisSerializer;
  22. import javax.crypto.KeyGenerator;
  23. import java.lang.reflect.Method;
  24. import java.time.Duration;
  25. import java.util.HashMap;
  26. import java.util.HashSet;
  27. import java.util.Map;
  28. import java.util.Set;
  29. @Configuration
  30. @EnableCaching
  31. public class RedisConfig extends CachingConfigurerSupport {
  32. /**
  33. * 重寫Redis序列化方式,使用Json方式:
  34. * 當我們的資料儲存到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到資料庫的。RedisTemplate預設使用的是JdkSerializationRedisSerializer,StringRedisTemplate預設使用的是StringRedisSerializer。
  35. * Spring Data JPA為我們提供了下面的Serializer:
  36. * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
  37. * 在此我們將自己配置RedisTemplate並定義Serializer。
  38. *
  39. */
  40. // 存入redis時,預設使用的是JdkSerializationRedisSerializer,使得存入的資料全部序列化了,所需自定義一個RedisTemplate,使用其他序列化方式
  41. //當redis依賴包匯入的時候,預設的cache即可自動變成redis模式;如果只是匯入cache的依賴,則預設的是simpleCacheManager;
  42. // 使用redis快取時,RedisCacheManager生成RedisCache後生成快取時預設使用JdkSerializationRedisSerializer序列化(cache儲存的時候)
  43. //當ioc容器內沒有自定義的快取管理器的時候---預設使用自帶的;
  44. //當通過@Bean在ioc容器中注入了以下管理器,則會使用自定義的管理器;
  45. // @Bean
  46. // public CacheManager cacheManager(RedisConnectionFactory factory) {
  47. // RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一個預設配置,通過config物件即可對快取進行自定義配置
  48. // config = config.entryTtl(Duration.ofMinutes(1)) // 設定快取的預設過期時間,也是使用Duration設定
  49. // .disableCachingNullValues(); // 不快取空值
  50. //
  51. // // 設定一個初始化的快取空間set集合
  52. // Set<String> cacheNames = new HashSet<>();
  53. // cacheNames.add("my-redis-cache1");
  54. // cacheNames.add("my-redis-cache2");
  55. //
  56. // // 對每個快取空間應用不同的配置
  57. // Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
  58. // configMap.put("my-redis-cache1", config);
  59. // configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
  60. //
  61. // RedisCacheManager cacheManager = RedisCacheManager.builder(factory) // 使用自定義的快取配置初始化一個cacheManager
  62. // .initialCacheNames(cacheNames) // 注意這兩句的呼叫順序,一定要先呼叫該方法設定初始化的快取名,再初始化相關的配置
  63. // .withInitialCacheConfigurations(configMap)
  64. // .build();
  65. // return cacheManager;
  66. // }
  67. @Bean
  68. @Primary//當有多個管理器的時候,必須使用該註解在一個管理器上註釋:表示該管理器為預設的管理器
  69. public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
  70. //初始化一個RedisCacheWriter
  71. RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
  72. //序列化方式1
  73. //設定CacheManager的值序列化方式為JdkSerializationRedisSerializer,但其實RedisCacheConfiguration預設就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下(4行)註釋程式碼為預設實現
  74. // ClassLoader loader = this.getClass().getClassLoader();
  75. // JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
  76. // RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
  77. // RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
  78. //序列化方式1---另一種實現方式
  79. //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//該語句相當於序列化方式1
  80. //序列化方式2
  81. FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);//JSONObject
  82. RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
  83. RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
  84. //序列化方式3
  85. //Jackson2JsonRedisSerializer serializer=new Jackson2JsonRedisSerializer(Object.class);
  86. //RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer);
  87. //RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
  88. defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(100));//設定過期時間
  89. // //設定預設超過期時間是30秒
  90. // defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
  91. //初始化RedisCacheManager
  92. RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
  93. //設定白名單---非常重要********
  94. /*
  95. 使用fastjson的時候:序列化時將class資訊寫入,反解析的時候,
  96. fastjson預設情況下會開啟autoType的檢查,相當於一個白名單檢查,
  97. 如果序列化資訊中的類路徑不在autoType中,
  98. 反解析就會報com.alibaba.fastjson.JSONException: autoType is not support的異常
  99. 可參考 https://blog.csdn.net/u012240455/article/details/80538540
  100. */
  101. ParserConfig.getGlobalInstance().addAccept("zmc.leon.mcd.entity.");
  102. return cacheManager;
  103. }
  104. /**
  105. * 設定 redis 資料預設過期時間
  106. * 設定@cacheable 序列化方式
  107. * @return
  108. */
  109. @Bean
  110. public RedisCacheConfiguration redisCacheConfiguration(){
  111. FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
  112. RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
  113. configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(30));
  114. return configuration;
  115. }
  116. @Bean(name = "redisTemplate")
  117. @SuppressWarnings("unchecked")
  118. @ConditionalOnMissingBean(name = "redisTemplate")
  119. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  120. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  121. //使用fastjson序列化
  122. FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
  123. // value值的序列化採用fastJsonRedisSerializer
  124. template.setValueSerializer(fastJsonRedisSerializer);
  125. template.setHashValueSerializer(fastJsonRedisSerializer);
  126. // key的序列化採用StringRedisSerializer
  127. template.setKeySerializer(new StringRedisSerializer());
  128. template.setHashKeySerializer(new StringRedisSerializer());
  129. template.setConnectionFactory(redisConnectionFactory);
  130. return template;
  131. }
  132. // @Bean
  133. // public KeyGenerator KeyGenerator() {
  134. // return new KeyGenerator(){
  135. // public Object generate(Object target, Method method, Object... params) {
  136. // StringBuilder sb = new StringBuilder();
  137. // sb.append(target.getClass().getName());
  138. // sb.append(method.getName());
  139. // for (Object obj : params) {
  140. // sb.append(obj.toString());
  141. // }
  142. // return sb.toString();
  143. // }
  144. // };
  145. // }
  146. }

FastJsonRediaSerializer.java

  1. package zmc.leon.mcd.Util;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializerFeature;
  4. import org.springframework.data.redis.serializer.RedisSerializer;
  5. import org.springframework.data.redis.serializer.SerializationException;
  6. import java.nio.charset.Charset;
  7. /*
  8. 要實現物件的快取,定義自己的序列化和反序列化器。使用阿里的fastjson來實現的比較多。
  9. */
  10. public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
  11. private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  12. private Class<T> clazz;
  13. public FastJsonRedisSerializer(Class<T> clazz) {
  14. super();
  15. this.clazz = clazz;
  16. }
  17. @Override
  18. public byte[] serialize(T t) throws SerializationException {
  19. if (null == t) {
  20. return new byte[0];
  21. }
  22. return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  23. }
  24. @Override
  25. public T deserialize(byte[] bytes) throws SerializationException {
  26. if (null == bytes || bytes.length <= 0) {
  27. return null;
  28. }
  29. String str = new String(bytes, DEFAULT_CHARSET);
  30. return (T) JSON.parseObject(str, clazz);
  31. }
  32. }

注意:

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

具體解釋請關注: