Spring整合Redis

Redis 介绍

Redis 是目前业界使用最广泛的内存数据存储。相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets ,String等,
同时支持数据持久化。除此之外,Redis 还提供一些类数据库的特性,比如事务,HA,主从库。可以说 Redis 兼具了缓存系统和数据库的一些特性,
因此有着丰富的应用场景。本文介绍 Redis 在 Spring Boot 中两个典型的应用场景。

搭建Redis服务端

下载redis文件 我这里用的是 Redis-x64-3.2.100
下载成功解压进入文件后,在路径框上输入cmd 进入命令行操作
输入redis-server.exe redis.windows.conf 即启动成功

springboot整合Redis

1、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!--        test-->
<dependency>
  <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
</dependency>
<!--     开启web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Spring Boot 提供了对 Redis 集成的组件包:spring-boot-starter-data-redis,spring-boot-starter-data-redis依赖于spring-data-redis 和 lettuce 。
Spring Boot 1.0 默认使用的是 Jedis 客户端,2.0 替换成 Lettuce,但如果你从 Spring Boot 1.5.X 切换过来,几乎感受不大差异,这是因为 spring-boot-starter-data-redis 为我们隔离了其中的差异性。
Lettuce 是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 netty NIO 框架来高效地管理多个连接。

2、添加配置

#Redis
#Redis数据库索引(默认为0)
  redis:
    database: 0
  # Redis服务器地址
    host: localhost
  # Redis服务器连接端口
    port: 6379
  # Redis服务器连接密码(默认为空)
    password:
  # 连接池最大连接数(使用负值表示没有限制) 默认 8
    lettuce:
      pool:
        max-active: 8
  # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1
  # 连接池中的最大空闲连接 默认 8
        max-idle: 8
  # 连接池中的最小空闲连接 默认 0
        min-idle: 0

3、添加 cache 的配置类

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;

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

注意我们使用了注解:@EnableCaching来开启缓存。

4、实体类省略 自行创建 此处我用的实体类是Style

@Entity
public class Style implements Serializable {
    @Id
  @GeneratedValue(strategy=IDENTITY)
    private int id;
   @Column(length = 32)
    private String style;
    private Double money;
    private String facilities;
    private String image;
    private String message;

  .....get()+set()方法..........

此处实体类要继承implements Serializable 因为redis保存对象需要把对象数据转换成字节数组才可操作

5、单元测试代码

package com.hotel.demo;

import com.hotel.demo.bean.Style;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest()
public class DemoApplicationTests {
    //   RedisTemplate是操作实体对象存储到redis,即操作的对象要转换成字节数组
//    StringRedisTemplate操作的是字符串然后数据在redis中可读的数据,操作非字节数组
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void redisTest() throws InterruptedException {
         /*  TimeUnit.SECONDS(5)线程等待五秒
        TimeUnit.MILLISECONDS(5000)线程等待五秒.
        1 表示时间基数  表示缓存有效时间
        */
        stringRedisTemplate.opsForValue().set("aaa", "111", 5, TimeUnit.SECONDS);
        //判断key=aaa的数据是否存在,存在继续执行,不存在报异常
        Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
        System.out.println("缓存存在");
        /*     Thread.sleep(1000);*/
   /*     Assert.assertEquals("111",stringRedisTemplate.opsForValue().get("aaa"));
        System.out.println("缓存不存在");*/

    }

    @Test
    //缓存实体类对象   切记实体类必须implements Serializable
    public void testObj() throws Exception {
        Style style = new Style();
        style.setStyle("大房");
        style.setMoney(125.0);
        style.setFacilities("卫生间、健生房");
        style.setMessage("可住两人");
        //创建一个指定的缓存对象
        ValueOperations<String, Style> operations = redisTemplate.opsForValue();
        operations.set("com.neox", style);
        operations.set("com.neo.f", style, 1, TimeUnit.SECONDS);
        //主线程睡眠1秒
        Thread.sleep(1000);
        //删除缓存  by Key
//        redisTemplate.delete("com.neox");
        //判断指定内容是否存在缓存中
        boolean exists = redisTemplate.hasKey("com.neo.f");
        if (exists) {
            System.out.println("exists is true");
        } else {
            System.out.println("exists is false");
        }
    }

    @Test
    //缓存集合对象  因为List中的元素是一个实体对象 所以使用的是RedisTemplate存储
    public void redisTestList() throws InterruptedException {
        List<String> list1 = new ArrayList<String>();
        list1.add("a1");
        list1.add("a2");
        list1.add("a3");
        //通常存储数据会先判断数据是否存在,不存在再存储
        if (redisTemplate.hasKey("listkey1") != true) {
/*            不管是leftPush还是rightPush都可以用leftPop或者rightPoP任意一种获取到其中的值,
             不过就是获取的遍历方向不一样。有学过数据结构的人都知道里面循环链表是可以前后遍历的,
            就和这里的场景是一样的。如果还有不懂的话可以去看看这部分的源代码,
             其实就是遍历方向不同,所以效率也不同。所以最好leftPush用leftPoP遍历,rightPush用rightPoP遍历*/
            redisTemplate.opsForList().rightPush("listkey1", list1);
            // redisTemplate.opsForList().leftPush("listkey1",list1);
            List<String> resultList1 = (List<String>) redisTemplate.opsForList().rightPop("listkey1");
            //  List<String> resultList2=(List<String>)redisTemplate.opsForList().leftPop("listkey1");
            System.out.println("resultList1:" + resultList1);
        } else {
            System.out.println("数据已存在缓存中");
        }

    }


    //缓存集合对象  因为List中的元素是一个实体对象 所以使用的是RedisTemplate存储
    @Test
    public void redisTestSet() throws InterruptedException {
        SetOperations<String, String> setOperations = redisTemplate.opsForSet();
        setOperations.add("set1", "1");
        setOperations.add("set1", "2");
        setOperations.add("set1", "3");
        Set<String> newset = redisTemplate.opsForSet().members("set1");
        System.out.println(newset);
    }

    //缓存集合对象  因为List中的元素是一个实体对象 所以使用的是RedisTemplate存储
    @Test
    public void redisTestHash() throws InterruptedException {
        Map<String, String> map = new HashMap<String, String>();
        map.put("key1", "1");
        map.put("key2", "2");
        map.put("key3", "3");
        redisTemplate.opsForHash().putAll("map", map);
        //返回值格式为键值对   resultMap{key1=1, key2=2, key3=3}
        Map<String, String> resultMap = redisTemplate.opsForHash().entries("map");
        //返回值为所有value resulreslutMapListtMap:[1, 2, 3]
        List<String> reslutMapList = redisTemplate.opsForHash().values("map");
        //返回值为所有键 resultMapSet:[key1, key2, key3]
        Set<String> resultMapSet = redisTemplate.opsForHash().keys("map");
        //返回值为指定键的值
        String value = (String) redisTemplate.opsForHash().get("map", "key1");
        //list ——其中的值允许重复,因为它是有自己的排序规则的数据结构,所以可用来接受value值
        // Set——其中的值不允许重复,无序的数据结构   只能用来接受键值,因为键值不可重复
        System.out.println("resultMap" + resultMap);
        System.out.println("resulreslutMapListtMap:" + reslutMapList);
        System.out.println("resultMapSet:" + resultMapSet);
        System.out.println("value:" + value);
    }
}

6、自动根据方法生成缓存

使用注解@Cacheable() @CacheEvict() @CachePut()
至于使用方式可参考我另一篇文章https://blog.youkuaiyun.com/weixin_43784880/article/details/104282372

代码 此处仅展示一个用法
使用注解@Cacheable(value = “style”) 注解于要实现缓存的方法上,返回值会自动存储至redis,value表示数据的key值
如果不指定value 则报错At least one cache should be provided per cache operation.(每个缓存操作至少应该提供一个缓存。)
因为redis存储键不能为空,执行效果 发送两次findById请求,仅打印一次时间差,因为第一次缓存后,第二次程序直接从缓存中读取数据,不执行方法

 //按照ID查找数据
    @ApiOperation(value = "按照ID查找数据", notes = "按照ID查找数据")
    @ApiImplicitParam(name = "id", value = "类型ID", required = true, dataType = "int", paramType = "path")
    @RequestMapping(value = "/findById/{id}", method = RequestMethod.GET)
//打开该方法的缓存
    @Cacheable(value = "style")    
    public Style findById(@PathVariable int id) {
        long startTime = System.currentTimeMillis();
        Style style = styleService.findById(id);
        long endTime = System.currentTimeMillis();
        logger.info("start -->end时间差" + (endTime - startTime));
        return style;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值