【问题标题】:How to Store non-serialized object into rediscache using kryoSerialzation如何使用 kryoSerialzation 将非序列化对象存储到 rediscache 中
【发布时间】:2018-01-14 08:22:53
【问题描述】:

我在我的 Spring Boot 应用程序上使用了 redis 缓存,我需要将未知类型的响应对象存储到 redisTemplate 中。所以我在我的 redisTemplates 上使用 kryoSerialzation,

例外:

org.objenesis.ObjenesisException: java.io.NotSerializableException: class sample.data.redis.model.Student not serializable
at org.objenesis.strategy.SerializingInstantiatorStrategy.newInstantiatorOf(SerializingInstantiatorStrategy.java:58)
at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1127)
at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1136)

注意:

我无法在我的响应对象上实现 Serializable 接口,因为它在我的应用程序上是动态且未知的类型

pom.xml

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
         <groupId>redis.clients</groupId>
         <artifactId>jedis</artifactId>
     </dependency>
     <dependency>
        <groupId>com.esotericsoftware</groupId>
        <artifactId>kryo</artifactId>
        <version>4.0.1</version>
    </dependency>

KryoRedisSerializer.java

    public class KryoRedisSerializer implements RedisSerializer<Object>{

    private  final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() {
         @Override
         protected Kryo initialValue() {
             Kryo kryo = new Kryo();
             kryo.register(Object.class);
             return kryo;
         }
     };
     public KryoRedisSerializer() {
         kryoThreadLocal.get().setInstantiatorStrategy((InstantiatorStrategy) new SerializingInstantiatorStrategy());
       }

    @Override
    public byte[] serialize(Object object) throws SerializationException {
        // TODO Auto-generated method stub
         if (object == null) {
             return new byte[0];
          }

          try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
             Output output = new Output(outputStream);
             kryoThreadLocal.get().writeClassAndObject(output, object);
             return output.toBytes();
          } catch (IOException e) {
             throw new SerializationException("Failed Serialization", e);
          }
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        // TODO Auto-generated method stub
          if (bytes == null || bytes.length == 0) {
             return null;
          }
          try (Input input = new Input(bytes)) {
             return kryoThreadLocal.get().readClassAndObject(input);
          }
      }
    }
}

SpringRadisConfig.java

 @Configuration
public class SpringRadisConfig {
    @Bean
    public JedisConnectionFactory connectionFactory() {
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
        connectionFactory.setHostName("localhost");
        connectionFactory.setPort(6379);
        return connectionFactory;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory());

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        redisTemplate.setValueSerializer(kryoRedisSSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
    @Bean
    public RedisSerializer<Object> kryoRedisSSerializer() {
       return new KryoRedisSSerializer();
    }
}

SampleRedisApplication.java(主方法)

@SpringBootApplication
@ComponentScan(basePackages = "sample.data.redis")
public class SampleRedisApplication implements CommandLineRunner {

    @Autowired
    private StringRedisTemplate template;

    @Autowired
    private SpringRadisConfig redisConfig;

    @Override
    public void run(String... args) throws Exception {/*
        ValueOperations<String, String> ops = this.template.opsForValue();
        String key = "spring.boot.redis.test";
        if (!this.template.hasKey(key)) {
            ops.set(key, "Hi Ratheesh ");
        }
        System.out.println("Found key " + key + ", value=" + ops.get(key));*/

        RedisTemplate<String, Object> redisTemplate = redisConfig.redisTemplate();
        ValueOperations<String, Object> values = redisTemplate.opsForValue();

        try { 
            Student student = new Student(1L, "Vishal");
            values.set("student", student);
            Student std = (Student) values.get("student");

            System.out.println(std.getId());
            System.out.println(std.getName());

        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        // Close the context so it doesn't stay awake listening for redis
        SpringApplication.run(SampleRedisApplication.class, args).close();
    }

}

假设学生是我从远程 API 获得的响应对象,实际上我的应用程序中没有任何模型对象定义 (POJO) 用于 API 响应,因为我的 API 响应类型是 (Type T) 即动态节点

我在github 中找到了一个解决方案,但这不起作用(将对象转换为数组并将其存储在 Redis 中)

【问题讨论】:

    标签: java spring-boot redis jedis kryo


    【解决方案1】:

    后来我决定使用 Redisson 而不是 Jedis 客户端,这样可以解决我的序列化问题

    用于cachable/cachPut/cacheEvict注解类型实现的CacheManager配置

    @Configuration
    @EnableCaching
    public class RedissonCacheConfiguration {
    
    
        /**
         * Property holder for holding the Redis cache configuration properties like
         * <code>Host, Port, Session Duration</code>, etc.
         * 
         * <p>
         * Bean with name <code>redisCacheConfigProperties</code> should be populated with these
         * configuration properties and then loaded in the application context.
         */
        @Autowired
        private RedissonConfigProperties redisCacheConfigProperties;
    
        /**
         * Sets the Redis cache configuration properties.
         * 
         * @param redisCacheConfigProperties Property holder for holding the Redis cache configuration
         *        properties.
         */
        public void setRedissonConfigProperties(
                final RedissonConfigProperties redisCacheConfigProperties) {
            this.redisCacheConfigProperties = redisCacheConfigProperties;
        }
    
        @Bean
        public CacheManager cacheManager() {
    
            CacheManager cacheManager = new RedissonSpringCacheManager(redissonClient(), getCacheConfigs());
            return cacheManager;
        }
    
        @Bean 
        public Map<String, CacheConfig> getCacheConfigs(){
            Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
            // ttl = 24 mins, maxIdleTime = 12 mins
            config.put("testCache", new CacheConfig(24*60*1000, 12*60*1000));
            return config;
        }
    
        @Bean
        public RedissonClient redissonClient() {
            Config config = new Config();
                SingleServerConfig singleServerConfig = config.useSingleServer();
                // format as redis://127.0.0.1:7181 or rediss://127.0.0.1:7181 for SSL
                String schema = redisCacheConfigProperties.isSsl() ? "rediss://" : "";
                singleServerConfig.setAddress(schema + redisCacheConfigProperties.getHost() + ":" + redisCacheConfigProperties.getPort());
                singleServerConfig.setDatabase(redisCacheConfigProperties.getDatabase());
                if (redisCacheConfigProperties.getPassword() != null) {
                    singleServerConfig.setPassword(redisCacheConfigProperties.getPassword());
                }
            return Redisson.create(config);
        }
    }
    

    可缓存注释用法

    @Cacheable(cacheNames = "#cacheProperies.cacheName", key = "#cacheProperies.cacheKey", unless = "#result==null")
        public Object fetchData( final CacheProperties cacheProperies) {
            // If there is no cached data by the given key, then we will return NULL.
            return doTheOperation();
        }
    
        @CacheEvict(cacheNames = "#cacheProperies.cacheName", key = "#cacheProperies.cacheKey")
        public void removeData(final CacheProperties cacheProperies) {
            // Deletes the Cached CartInfo.
        }
    
        @CachePut(cacheNames = "#cacheProperies.cacheName", key = "#cacheProperies.cacheKey", unless = "#result==null")
        public Object cacheData(final Object object, final CacheProperties cacheProperies ) {
            // Returns the same  object which is received as parameter,
            // so that this  object will be cached by key '... reference'.
            return doTheOperation();
        }
    

    【讨论】:

      猜你喜欢
      • 2012-04-07
      • 2014-08-05
      • 2017-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-15
      • 2012-11-06
      • 1970-01-01
      相关资源
      最近更新 更多