springboot2.x使用redis作为缓存(使用fastjson序列化的方式,并调试反序列化异常)

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 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 cacheNames =  new HashSet<>();
//        cacheNames.add("my-redis-cache1");
//        cacheNames.add("my-redis-cache2");
//
//        // 对每个缓存空间应用不同的配置
//        Map 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 pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
//        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
    //序列化方式1---另一种实现方式
        //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//该语句相当于序列化方式1

        //序列化方式2
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);//JSONObject
        RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

    //序列化方式3
        //Jackson2JsonRedisSerializer serializer=new Jackson2JsonRedisSerializer(Object.class);
        //RedisSerializationContext.SerializationPair 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 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 redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate 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 implements RedisSerializer {
    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private Class clazz;
    public FastJsonRedisSerializer(Class 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

 

具体解释请关注:

https://blog.csdn.net/u012240455/article/details/80538540

 
 
 

 

 

你可能感兴趣的:(springboot)