• springboot 用redis缓存整合spring cache注解,使用Json序列化和反序列化。


    springboot下用cache注解整合redis并使用json序列化反序列化。

    cache注解整合redis

    最近发现spring的注解用起来真的是很方便。随即产生了能不能吧spring注解使用redis实现的方式。
    只需要在配置文件中(application.propertoes)添加如下一个配置

    spring.cache.type=redis
    

    并配置好redis的相关信息

    spring.redis.database=0
    spring.redis.host=
    spring.redis.port=
    spring.redis.password=
    spring.redis.timeout=5000ms
    spring.redis.lettuce.pool.max-active=8
    spring.redis.lettuce.pool.max-wait=-1ms
    spring.redis.lettuce.pool.max-idle=8
    spring.redis.lettuce.pool.min-idle=0
    

    springcache注解整合redis非常容易就整合完成了。

    redis缓存序列化与反序列化

    由于缓存数据使用的是jdk自带的序列化 需要序列化的实体类继承Serializable接口。而且序列化后的内容在redis中看起来也不是很方便。
    于是萌生了需要将数据序列化成json的想法。
    经过一番研究后决定写一个redis 配置文件。RedisConfig具体内容如下

    
    @Configuration
    public class RedisConfig  extends CachingConfigurerSupport {
    
      @Bean
      public RedisCacheConfiguration redisCacheConfiguration(){
          Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
          RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
          configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).entryTtl(Duration.ofDays(30));
          return configuration;
      }
    
      @Bean
      public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
          //初始化一个RedisCacheWriter
          RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
          //设置CacheManager的值序列化方式为 fastJsonRedisSerializer,但其实RedisCacheConfiguration默认使用StringRedisSerializer序列化key,
          Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
          RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer);
          RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
          return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
      }
    }
    
    

    配置完成后,数据访问序列化都非常正常,redis中也可以看到有序的json数据。

    {
      "name": "long",
      "age": 18,
      "height": 1.72
    }
    

    但是。。。。

    当再次访问时出现了一个奇怪的问题。
    LinkedHashMap 不能转换为实体类。

    java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.alance.springcachedemo.entity.User
    

    这看起来很像泛型丢失啊。
    随即第三版配置文件出炉了,配置一下序列化的泛型保存

    @Configuration
    public class RedisConfig  extends CachingConfigurerSupport {
    
    
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(factory);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            MyObjectMapper objectMapper = new MyObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            MyObjectMapper objectMapper = new MyObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
            // 配置序列化
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
            RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
            return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
    
        }
    
    
        private class MyObjectMapper extends ObjectMapper {
    
            private static final long serialVersionUID = 1L;
    
            public MyObjectMapper() {
                super();
                // 去掉各种@JsonSerialize注解的解析
                this.configure(MapperFeature.USE_ANNOTATIONS, false);
                // 只针对非空的值进行序列化
                this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                // 将类型序列化到属性json字符串中
                this.enableDefaultTyping(DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
                // 对于找不到匹配属性的时候忽略报错
                this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                // 不包含任何属性的bean也不报错
                this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            }
        }
    
    }
    
    

    好了这下反序列化的问题解决了,
    redis中

    [
      "com.alance.springcachedemo.entity.User",
      {
        "name": "long",
        "age": 18,
        "height": 1.72
      }
    ]
    

    redis存储的json中带上了类型
    至此这个问题就解决了。

    后记

    在愉快使用缓存注解的时候,发现缓存注解并 不能和诸如事务注解线程池注解一起使用。这是aop代理的特性决定的。而且 方法的类内部调用也不走注解

    enableDefaultTyping 这个功能涉及到java著名的反序列化漏洞。各位系统之间调用数据的项目还是慎重使用.
    漏洞详情

    后记的后记

    在写代码的过程中发现了一个更优雅的解决方案分享给大家。
    只需要在配置文件中配置一下CacheManger,使用jackson的一个带泛型的序列化工具实现。

        /**
         * spring cache 注解相关序列化操作
         */
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // 配置序列化
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
            RedisCacheConfiguration redisCacheConfiguration = config
                    // 键序列化方式 redis字符串序列化
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                    // 值序列化方式 简单json序列化
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));
            return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
    
        }
    
    大佬的启发

    https://www.cnblogs.com/Kidezyq/p/8942111.html

  • 相关阅读:
    数据结结构学习 赫夫曼树
    C++ 类中特殊的成员变量(常变量、引用、静态)的初始化方法
    Linux/Unix time时间戳的处理转换函数
    gcc中include文件的搜索路径
    数据结结构学习 2叉树
    C++ 虚函数表解析
    数据结结构学习 线性表
    C#实现MD5加密
    学习的艺术
    C# 3.0实现类本身的方法扩展
  • 原文地址:https://www.cnblogs.com/yanlong300/p/11905978.html
Copyright © 2020-2023  润新知