11

实例讲解Springboot以Template方式整合Redis及序列化问题

 4 years ago
source link: http://www.cnblogs.com/larrydpk/p/12770541.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 简介

之前讲过如何通过 Docker 安装 Redis ,也讲了 SpringbootRepository 方式整合 Redis ,建议阅读后再看本文效果更佳:

(1) Docker安装Redis并介绍漂亮的可视化客户端进行操作

(2) 实例讲解Springboot以Repository方式整合Redis

本文将通过实例讲解 SpringbootTemplate 方式整合 Redis ,并遇到一些序列化的问题。代码结构如下:

NnMvYbB.png!web

2 整合过程

与文章《 实例讲解Springboot以Repository方式整合Redis 》相同的代码不再列出来,文末将提供代码下载方式。

2.1 自动配置类

把相关依赖引入到项目中后, Springboot 就自动帮我们生成了 Template 类,分别是 RedisTemplateStringRedisTemplate 。看一下自动配置类能看出这两个类都已经创建到Spring容器里了。

public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean( name = {"redisTemplate"} )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

实际上 StringRedisTemplateRedisTemplate 的子类,对于 String 类型,更推荐使用前者,它的类型只能是 String 的,会有类型检查上的安全;而 RedisTemplate 可以操作任何类型。

2.2 实现数据访问层

本文通过 RedisTemplateRedis 进行操作,所以我们需要将它注入进来。代码如下:

package com.pkslow.springbootredistemplate.dal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAL {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    public void setValue(Object key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getValue(Object key) {
        return redisTemplate.opsForValue().get(key);
    }
}

RedisTemplate 提供了丰富的方法,具体可以参考官方文档,本次用到的及类似的方法有:

  • opsForHash() : 返回对于Hash的操作类;
  • opsForList() : 返回对于列表List的操作类;
  • opsForSet() : 返回对于Set的操作类;
  • opsForValue() : 返回对于字符串String的操作类;
  • opsForZSet() : 返回对于ZSet的操作类。

2.3 实现Controller

我们需要把功能通过 Web 的方式暴露出去,实现以下 Contrller

package com.pkslow.springbootredistemplate.controller;

import com.pkslow.springbootredistemplate.dal.UserDAL;
import com.pkslow.springbootredistemplate.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/userTemplate")
public class UserTemplateController {

    @Autowired
    private final UserDAL userDAL;

    public UserTemplateController(UserDAL userDAL) {
        this.userDAL = userDAL;
    }

    @GetMapping("/{userId}")
    public User getByUserId(@PathVariable String userId) {
        return (User)userDAL.getValue(userId);
    }

    @PostMapping("/{userId}")
    public User addNewUser(@PathVariable String userId,
                           @RequestBody User user) {
        user.setUserId(userId);
        userDAL.setValue(userId, user);
        return user;
    }

}

只提供两个接口,分别是设值和取值。

2.4 通过Postman测试

(1)存入对象

7nUBnej.png!web

(2)读取对象

3Q7zmy3.png!web

能写能读,功能实现,完美!Perfect!收工!

3 序列化问题

程序功能正常运行一段时间后,运维杀来了: “这是什么东西?我怎么看得懂?我要怎么查看数据?”

IvaABnm.png!web

3.1 定位问题

不得不重新打开项目代码, Debug 一下看看哪出了问题。既然用 Postman 测试能正常显示,而数据库显示不对,说明是写入数据库时做了转换。查看 RedisTemplate 就行了,毕竟活是他干的(先疯狂甩锅)。

VbeUbmI.png!web

看它的序列化类用的是默认的 JdkSerializationRedisSerializer ,所以序列化后的数据我们看不懂。

3.2 问题修复

甩锅完后,还是要修复问题的,毕竟代码是自己写的。关键就是替换掉 RedisTemplate 所使用的序列化类就行了,这有两个方案可选:

(1)自定义一个新的 RedisTemplate 以覆盖旧的,在定义的时候指定序列化类。大致代码如下:

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
  ObjectMapper om = new ObjectMapper();
  om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  jackson2JsonRedisSerializer.setObjectMapper(om);

  RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  template.setConnectionFactory(redisConnectionFactory);
  template.setKeySerializer(jackson2JsonRedisSerializer);
  template.setValueSerializer(jackson2JsonRedisSerializer);
  template.setHashKeySerializer(jackson2JsonRedisSerializer);
  template.setHashValueSerializer(jackson2JsonRedisSerializer);
  template.afterPropertiesSet();
  return template;
}

甚至还可以自定义 RedisConnectionFactory ,如下:

@Bean
JedisConnectionFactory jedisConnectionFactory() {
    JedisConnectionFactory jedisConFactory = new JedisConnectionFactory();
    jedisConFactory.setHostName("localhost");
    jedisConFactory.setPort(6379);
    return jedisConFactory;
}

(2)使用原有的 RedisTemplate ,在使用前替换掉序列化类

引用的类的代码如下, init 方法作为初始化方法:

public class UserDAL {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    public void init() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));

    }

    public void setValue(Object key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public Object getValue(Object key) {
        return redisTemplate.opsForValue().get(key);
    }
}

然后在创建 UserDAL 时,代码如下:

@Bean(initMethod = "init")
public UserDAL userDAL() {
  return new UserDAL();
}

重新提交代码、重新测试、重新发布,结果可以了:

m6j2Efb.png!web

4 总结

本文详细代码可在 南瓜慢说 公众号回复< SpringbootRedisTemplate >获取。

欢迎访问 南瓜慢说 www.pkslow.com 获取更多精彩文章!

欢迎关注微信公众号< 南瓜慢说 >,将持续为你更新...

YvYNfa3.jpg!web

多读书,多分享;多写作,多整理。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK