为Spring-boot Redis缓存配置配置新的序列化器

时间:2019-03-10 13:32:47

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

我一直在尝试更改spring-boot redis缓存的默认序列化程序,因为我想从Default更改为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时,我无法使用此序列化器反序列化该序列化器,序列化器只给我一个Person对象,该对象具有空名称,电子邮件和id属性。有办法解决这个问题吗?

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

3 个答案:

答案 0 :(得分:2)

GenericJackson2JsonRedisSerializer假设杰克逊的default typing。使用GenericJackson2JsonRedisSerializer实例创建ObjectMapper时,请确保配置默认类型(enableDefaultTyping(…))。

默认类型输入最适合非最终类型,并且在所有JSON有效负载中都要求该类型具有一致的属性名称,以便Jackson可以识别要反序列化的适当类型。

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

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

答案 1 :(得分:1)

你可以使用 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);
    }
}

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

@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();
    }

答案 2 :(得分:0)

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

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

我的 RedisCacheConfiguration bean 看起来像:

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