【问题标题】:Configure a new serializer for spring-boot redis cache config为 spring-boot redis 缓存配置配置一个新的序列化程序
【发布时间】:2019-07-31 23:04:44
【问题描述】:

我一直在尝试更改 spring-boot redis 缓存的默认序列化程序,因为我想从默认更改为 Jackson2Json 实现之一。 Jackson2Json 库中有两个实现,其中之一是:GenericJackson2JsonRedisSerializer,我可以将其用于以下 bean 实例化:

@Bean
@Primary
public RedisCacheConfiguration defaultCacheConfig(ObjectMapper objectMapper) {

    return RedisCacheConfiguration.defaultCacheConfig()
        .serializeKeysWith(
            SerializationPair.fromSerializer(
                new StringRedisSerializer()
            )
        )
        .serializeValuesWith(
            SerializationPair.fromSerializer(
                new GenericJackson2JsonRedisSerializer(objectMapper)
            )
        )
        .prefixKeysWith("");
}

当我使用此序列化程序时,序列化工作正常,所有内容都存储在 redis 服务器上,但是当我尝试反序列化存储在 redis 服务器上的 JSON 时,我收到以下异常:

java.util.LinkedHashMap cannot be cast to tutorial.Person with root cause

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to tutorial.Person

缓存的使用方式如下:

@Cacheable(cacheNames = "person", key = "'person:'.concat(#post.id)")
public Person findPostAuthor(Post post){
}

序列化器不知道如何从 LinkedHashMap 转换为 Person,我该如何告诉他如何去做?

我尝试使用的另一个序列化器是 Jackson2JsonRedisSerializer:

@Bean
@Primary
public RedisCacheConfiguration defaultCacheConfig(ObjectMapper objectMapper) {
    Jackson2JsonRedisSerializer<Person> serializer = new Jackson2JsonRedisSerializer<>(Person.class);
    serializer.setObjectMapper(objectMapper);
    return RedisCacheConfiguration.defaultCacheConfig()
        .serializeKeysWith(
            SerializationPair.fromSerializer(
                new StringRedisSerializer()
            )
        )
        .serializeValuesWith(
            SerializationPair.fromSerializer(
                serializer
            )
        )
        .prefixKeysWith("");
}

通过这种方式,我必须为保存在 redis 缓存中的每个对象声明一个 bean,但我可以正确地序列化/反序列化。

当我直接在 redis 缓存中插入 JSON 时,我无法使用此序列化程序对其进行反序列化,序列化程序只会给我一个带有空名称、电子邮件和 id 属性的 Person 对象。有没有办法解决这个问题?

如果有办法改进我的问题,请告诉我。

【问题讨论】:

    标签: java spring-boot caching redis spring-data-redis


    【解决方案1】:

    GenericJackson2JsonRedisSerializer 假定为 Jackson 的 default typing。当您使用 ObjectMapper 实例创建 GenericJackson2JsonRedisSerializer 时,请确保配置默认类型 (enableDefaultTyping(…))。

    默认类型最适用于非最终类型,并且需要在所有 JSON 有效负载中为该类型提供一致的属性名称,以便 Jackson 可以识别要反序列化到的适当类型。

    默认类型使用动态类型标记,如果您的数据源(Redis 实例)不完全受信任,那么这可能会成为安全问题。

    Jackson2JsonRedisSerializer 被固定到特定类型并消除动态类型风险。

    【讨论】:

    • 如果我想使用 Jackson2JsonRedisSerializer,我必须为我使用的每个 Jackson2JsonRedisSerializer 声明一个新的 RedisCacheConfig,对吗?如何更改 @Cacheable 注释上的 RedisCacheConfig?
    【解决方案2】:

    你可以使用 Spring Data Redis

    添加依赖项:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    启用缓存和使用 Jackson2JsonRedisSerializer

    package com.redis.demo.redisdemo.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    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.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.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    @EnableCaching
    @Configuration
    public class RedisConfig extends CachingConfigurerSupport {
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            serializer.setObjectMapper(objectMapper);
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(serializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(serializer);
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
        @Bean
        public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
            RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
            RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
            return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
        }
    }
    

    在redis中缓存的方法处添加Cacheable注解

    @Cacheable(value = "employee", key = "#id")
        public Employee getEmployee(Integer id) {
            log.info("Get Employee By Id: {}", id);
            Optional<Employee> employeeOptional = employeeRepository.findById(id);
            if (!employeeOptional.isPresent()) {
                throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id Not foud");
            }
            return employeeOptional.get();
        }
    

    【讨论】:

      【解决方案3】:

      我知道这个问题已经很久没有问过了,但可能还有人需要答案。

      我遇到了同样的问题,我使用JdkSerializationRedisSerializer而不是GenericJackson2JsonRedisSerializer解决了它。

      我的RedisCacheConfiguration bean 看起来像:

      return RedisCacheConfiguration.defaultCacheConfig()
                  .entryTtl(Duration.ofHours("your-long-value"))
                  .serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()))
                  .serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer())); 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-28
        • 1970-01-01
        • 2021-05-23
        • 2021-12-16
        • 1970-01-01
        • 1970-01-01
        • 2022-09-25
        • 1970-01-01
        相关资源
        最近更新 更多