Springboot入门系列教程(5)-redis使用及入缓存注解介绍

一、Spingboot主要可用的两个redis客户端框架分别为jedis和Lettuce。

在2.0版本以前,start框架默认依赖的是jedis,而2.0及到现在最新的版本则是改为了依赖Lettuce。先说说两个客户端框架的主要区别。Jedis实现上是直连的Redis Server,在多线程环境下是非线程安全的。每个线程都需要拿自己的 Jedis实例,当连接数量增多时,资源消耗成本较高。Lettuce的连接是基于Netty的多线程、事件驱动的 I/O 框架(笔者后续的教程还会详细介绍Netty的用法)。1个redis客户端的连接可以在多个业务线程中共享使用,而且也是现成安全的。具体需要多少个连接实例可以根据并发情况按需增加。下面将主要介绍springboot2.0版本以后redis的先关使用。

二、首先POM文件引入starter依赖

        <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>

需要注意的一点,如果要使用redis的池,必须要自己手动添加commons-pool2的池组件依赖,不然会报找不到GenericObjectPoolConfig类的错。

三、YML文件关于redis的配置

spring:
  application:
    name: demo

  #redis lettuce
  redis:
    host: XX.XX.XX.XX
    database: 0
    password: XXXXXX
    port: XXX #你的redis的端口

    #客户端连接工具配置 springboot2.0以后默认使用这个工具。1.0使用Jedis
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

YML文件没有太多好说的,只是要注意池化配置里需要注意2.0以前是jedis,2.0及以后是lettuce

四、调用redis客户端

现在,只要在调用类中注入StringRedisTemplate模板对象,我们就可以通过该模板来直接对redis服务端进行kv操作了。该对象已经针对每种不同的redis数据格式都提供了相应方法,并且对异常进行了处理。

package com.ywcai.demo.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;


@Slf4j
@Service
public class RedisTest {


    //StringRedisTemplate RedisTemplate的区别,序列化方式不一样,
    //前者是通过String序列化,后者是通过字节进行序列化的。
    //建议常用直接使用string的模板即可
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    //redis查询所有的key
    public void getAllKey() {
        Set<String> keys = stringRedisTemplate.opsForValue().getOperations().keys("*");
        for (String s : keys
        ) {
            log.info("the key {}", s);
        }
    }
}

五、扩展应用,轻松使用缓存注解,来为数据库查询做缓存

Spingboot框架针对数据查询的缓存需要,结合redis做了3个注解,可以通过注解直接开启方法的数据缓存的功能。

package com.ywcai.demo.redis;


import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;


@Slf4j
@Service
public class RedisTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

 
    //spel 表达式中,#result 代表返回值
    //这里unless的表达式意思。如果返回值不是空 则存入缓存,为空,则不存入缓存
    @Cacheable(value = "users", key = "#testParam.id+'-'+#testParam.code", unless = "#result==null")
    public String getUser(TestParam testParam) {
        log.info("缓存没有被调用!");
        return testParam.msg;
    }

    //这里要删除,注意生成key的规则要和添加缓存的规则保持一致。意思是如果TestParam的数据有改动,则删除原来的缓存。。
    @CacheEvict(value = "users", key = "#testParam.id+'-'+#testParam.code")
    public void deleteUser(TestParam testParam) {
        log.info("删除了缓存的KEY!");
    }


    //调用@CachePut注解的方法时,返回结果作为value放入缓存并且更新了原来的key value 值。 下一次掉用@Cacheable方法的时候是直接调用缓存,而不需走一次方法@Cacheable注解的方法。
    @CachePut(value = "users", key = "#testParam.id+'-'+#testParam.code")
    public String updateUser(TestParam testParam) {
        testParam.msg = "set a new msg !";
        log.info("更新了msg ,并且用返回的值更新了缓存!");
        return testParam.msg;
    }

}

 

    condition 方法调用前判断,满足条件时,将结果存入缓存, condition 默认为""

    unless 方法调用后判断,满足时,不将结果存入缓存 unless 默认为""

    需要注意一点,如果是在RedisTest类本身的其他内部方法进行调用,是不会被缓存的,只有被外部类调用可以自动缓存

 

 

    value用于指定一个缓存的名称,可以理解为key的一个大的分组,后面再是通过keyGenerator方法用来生成具体的key值,也可以用key直接通过方法的名字、参数通过表达式进行指定。

六、keyGenerator的方法可以通过下面自定义配置类来自定义。

package com.ywcai.demo.redis;


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;

@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {

    //针对每个请求的类、方法和参数 生成唯一的key,这个不一定非要在这个配置里面加载,可以再需要使用的方法上面单独生成这个Bean也是可以的。
    @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() + "-");
                //这里要注意,如果添加KEY生成规则包含了 method方法,则使用cacheEvit的时候,插入数据方法和修改或者删除数据方法名字肯定不一样,最终key则肯定找不到。
                //因此最好采用  chche名字+调用类名+入参参数值的方式。因此这里注释掉了方法名
//                sb.append(method.getName() + "-");
                //这里入参的参数也要注意,如果是对象,最好对象要添加@DATA注解,这样重写了toString方法
                // toString方法最终使对象序列化的值,否则toString方法是对象在内存中的引用地址,肯定会不一样。
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }


}

七、在Test报里面的测试方法。

首先在src的包中增加了一个简单测试对象

 

package com.ywcai.demo.redis;

import lombok.Data;

@Data
public class TestParam {
    int id;
    String msg;
    int code;
}

下面是具体测试调用方法

package com.ywcai.demo.redis;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
class RedisTests {


    @Autowired
    RedisTest redisTest;

 
    @Test
    void testCachePut() {
        TestParam testParam1 = new TestParam();
        testParam1.setId(1);
        testParam1.setCode(1);
        testParam1.setMsg("set a init data !");
        //这里第一次打印数据,将不会掉缓存
        log.info("get user result 1 ===>>> {}", redisTest.getUser(testParam1));
        
        //下面第二次打印数据时,将直接使用缓存,原生方法不会被执行
        log.info("get user result 2 ===>>> {}", redisTest.getUser(testParam1));
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        //这里测试了入参一个新的对象。但值和第一次入参的值是一样的
        redisTest.updateUser(testParam1);
        
        //这里更新后第一次查看数据,因为使用了put方法,因此缓存会直接被调用
        log.info("after update the user invoke getUser  ===>>> {}", redisTest.getUser(testParam1));

        //这里调用了删除方法,因为加了cacheEvit注解,因此缓存的key会被删除
        redisTest.deleteUser(testParam1);
        //再次调用getUser方法,将执行原生方法
        log.info("after delete the user invoke getUser  ===>>> {}", redisTest.getUser(testParam1));
    }


}

运行后的效果截图

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值