springboot集成redis及使用json序列化

写在最前面:本文是基于spring boot 2.1.2.RELEASE

首先查看spring boot文档中关于redis的讲解,内容只有下面这些

You can inject an auto-configured RedisConnectionFactory, StringRedisTemplate, or vanilla RedisTemplate instance as you would any other Spring Bean. By default, the instance tries to connect to a Redis server at localhost:6379. The following listing shows an example of such a bean:

  @Component
  public class MyBean {

      private StringRedisTemplate template;

      @Autowired
      public MyBean(StringRedisTemplate template) {
          this.template = template;
      }

      // ...

  }

If you add your own @Bean of any of the auto-configured types, it replaces the default (except in the case of RedisTemplate, when the exclusion is based on the bean name, redisTemplate, not its type). By default, if commons-pool2 is on the classpath, you get a pooled connection factory.

主要就是说 可以直接注入RedisConnectionFactory、StringRedisTemplate或RedisTemplate(springboot自动配置)。那就开始吧

springboot 集成 redis 快速上手版

使用idea创建springboot工程,依赖勾选上redis

根据spring配置项(https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html),在application.properties中配置上redis的地址

spring.redis.database=0  
spring.redis.host=172.19.0.100
spring.redis.port=6379  
spring.redis.password=dayun

敲两个实体类(User.java Address.java)的代码,如下:

public class User implements Serializable {

    private String name;
    private int age;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}



public class Address implements Serializable {
    private String city;
    private String street;
    private String detail;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }
}

编写Test类代码,完成redis写入功能,如下

@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogredisApplicationTests {

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void contextLoads() {
    }


    @Test
    public void testRedis() {
        User user = new User();
        user.setName("edward");
        user.setAge(30);
        Address address = new Address();
        address.setCity("sjz");
        address.setStreet("长安区");
        address.setDetail("10-1-1302");
        user.setAddress(address);

        redisTemplate.opsForValue().set("user", user);
    }

}

运行提示成功,这样就完成springboot 集成redis了。不过查看redis中的写入结果,却发现是一堆看不懂的内容,如下:

image.png

这主要是因为将pojo对象序列化为了字节数组写入到redis中,具体在json序列化部分介绍

springboot 集成 redis json序列化版

上面得到一堆看不懂的写入结果,但我们想得到可视化的写入结果,所以又开始了序列化的研究,查阅springboot-data-redis文档发现:

In Spring Data, the conversion between the user (custom) types and raw data (and vice-versa) is handled Redis in the org.springframework.data.redis.serializer package.
This package contains two types of serializers that, as the name implies, take care of the serialization process:

  • Two-way serializers based on RedisSerializer.
  • Element readers and writers that use RedisElementReader and RedisElementWriter.

The main difference between these variants is that RedisSerializer primarily serializes to byte[] while readers and writers use ByteBuffer.

Multiple implementations are available (including two that have been already mentioned in this documentation):

  • JdkSerializationRedisSerializer, which is used by default for RedisCache and RedisTemplate.
  • the StringRedisSerializer.

However one can use OxmSerializer for Object/XML mapping through Spring OXM support or Jackson2JsonRedisSerializer or GenericJackson2JsonRedisSerializer for storing data in JSON format.
Do note that the storage format is not limited only to values. It can be used for keys, values, or hashes without any restrictions.

可以看到,RedisTemplate默认使用的是JdkSerializationRedisSerializer,将key和value都序列化为了字节数组,写入到redis中,就出现了上面我们看不懂的结果。使用Jackson2JsonRedisSerializer或者GenericJackson2JsonRedisSerializer可以得到json形式的序列化结果

有了上面的了解之后,下面要做的就是设置redisTemplate的序列化方式为Jackson2JsonRedisSerializer。

不幸的是查阅了springboot-data-redis文档,并设置redisTemplate和Jackson2JsonRedisSerializer的内容。。没有别的办法,只能百度查阅前辈们的使用方式,找到资料https://www.cnblogs.com/ityouknow/p/5748830.html:主要有想法的部分代码如下:

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
    
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    @SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        //设置缓存过期时间
        //rcm.setDefaultExpiration(60);//秒
        return rcm;
    }
    
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

虽然使用的springboot版本不太一样,但大的思路应该错不了,自己写个Redis的配置覆盖掉autoconfiguration中的RedisTemplate,这个springboot文档中也提到过,如下:

If you add your own @Bean of any of the auto-configured types, it replaces the default (except in the case of RedisTemplate, when the exclusion is based on the bean name, redisTemplate, not its type). By default, if commons-pool2 is on the classpath, you get a pooled connection factory.

好了,有了思路之后开干,写个自己的redis配置类:

@Configuration
public class RedisConfig {

    //还是使用springboot默认配置的RedisConnectionFactory
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    // 默认用的是用JdkSerializationRedisSerializer进行序列化的
    @Bean
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        // 注入数据源
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key-value结构序列化数据结构
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash数据结构序列化方式,必须这样否则存hash 就是基于jdk序列化的
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 启用默认序列化方式
        redisTemplate.setEnableDefaultSerializer(true);
        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);

        /// redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

配置完成之后,再次运行快速集成版中的测试用例,报错

Error:(25, 67) java: 无法访问com.fasterxml.jackson.databind.JavaType

看到错误后,推测肯定缺少jar包,百度了一下将Jackson Databind添加到pom中,再次运行成功写入,结果如下:

image.png

手动加入Jackson Databind,后来查阅springboot文档,发现:

Auto-configuration for Jackson is provided and Jackson is part of spring-boot-starter-json. When Jackson is on the classpath an ObjectMapper bean is automatically configured. Several configuration properties are provided for customizing the configuration of the ObjectMapper.

原来添加spring-boot-starter-json这个依赖就行,这样springboot会自动配置一个ObjectMapper。最终pom文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lqh</groupId>
    <artifactId>blogredis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>blogredis</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

springboot集成redis Hash处理版

由于我们系统中涉及到redis hash类型,所以要研究一下springboot如何写入redis hash类型。上面查阅springboot-data-redis文档的时候已经看到redis hash的内容,如下:

Data can be stored by using various data structures within Redis. Jackson2JsonRedisSerializer can convert objects in JSONformat. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case):

HashMapper有如下:

Hash mappers are converters of map objects to a Map<K, V> and back. HashMapper is intended for using with Redis Hashes.

Multiple implementations are available:

那就使用HashMapper来处理,有了上面两版的基础之后,这个就很容了,代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogredisApplicationTests {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ObjectMapper objectMapper;

    @Test
    public void contextLoads() {
    }


    @Test
    public void testRedisWrite() {
        User user = new User();
        user.setName("edward");
        user.setAge(30);
        Address address = new Address();
        address.setCity("sjz");
        address.setStreet("长安区");
        address.setDetail("10-1-1302");
        user.setAddress(address);

        HashMapper<Object, String, Object> mapper = new Jackson2HashMapper(objectMapper, false);

        redisTemplate.opsForHash().putAll("user_hash", mapper.toHash(user));
    }

    @Test
    public void testRedisRead() {
        Map map = redisTemplate.opsForHash().entries("user_hash");
        User user = objectMapper.convertValue(map, User.class);
        Assert.assertEquals(user.getAge(), 30);
    }
}

主要有一点提示就是:redisTemplate.opsForHash().entries()得到的是map,map转为pojo对象纠结了老半天,后来google发现jackson-bind的objectMapper提供了map转pojo的方法,这下直接用就可以了。

好了,写到这,系统涉及到的redis功能就预研完成了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351