返回 导航

SpringBoot / Cloud

hangge.com

SpringBoot - 缓存的使用详解2(使用Redis单机缓存)

作者:hangge | 2019-12-27 08:10
    在上文中我介绍了 Spring Boot 使用 EhCache 2.x 来作为缓存的实现(点击查看),本文接着介绍使用单机版的 Redis 作为缓存的实现。
    和 Ehcache 一样,如果在 classpath 下存在 Redis 并且 Redis 已经配置好了,此时默认就会使用 RedisCacheManager 作为缓存提供者。下面演示具体的使用步骤。

二、使用 Redis 单机缓存

1,添加依赖

首先编辑项目的 pom.xml 文件,添加 spring-boot-starter-cache 依赖以及 Redis 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

2,进行缓存配置

Redis 单机缓存只需要开发者在 application.properties 中添加 Redis 配置及缓存配置即可,代码如下:
(1)第 2 行配置的是缓存有效期,即 Rediskey 的过期时间。
(2)其它则是 Redis 的基本配置,更详细的介绍可以参考我之前写的这篇文章:
# 缓存配置
spring.cache.redis.time-to-live=1800s
# 基本连接信息配置
spring.redis.database=0
spring.redis.host=192.168.60.133
spring.redis.port=6379
spring.redis.password=123
# 连接池信息配置
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0

3,开启缓存

在项目的入口类上添加 @EnableCaching 注解开启缓存,代码如下:
@SpringBootApplication
@EnableCaching
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context
                = SpringApplication.run(DemoApplication.class, args);
    }
}

4,开始测试

(1)首先我们创建一个 Book 实体类:
@Setter
@Getter
@NoArgsConstructor
public class Book implements Serializable {
    private Integer id;
    private String name;
    private String author;
}

(2)接着创建一个 BookDao 并添加相关的缓存注解:
(1)在 BookDao 上添加 @CacheConfig 注解指定使用的缓存的名字,这个配置可选。若不使用 @CacheConfig 注解,则直接在 @Cacheable 注解中指明缓存名字。
(2)在方法上添加 @Cacheable 注解表示对该方法进行缓存:
  • 默认情况下,缓存的 key 是方法的参数,缓存的 value 是方法的返回值。
  • 当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有则直接使用缓存数据,该方法不会执行。
  • 否则执行该方法,执行成功后将返回值缓存起来。
  • 但若是在当前类中调用该方法,则缓存不会生效。
(3)@Cacheable 注解中还有一个属性 condition 用来描述缓存的执行时机,例如:
  • @Cacheable(condition="#id%2-0") 表示当 id 2 取模为 0 时才进行缓存,否则不缓存。
(4)如果开发者不想使用默认的 key,也可以像第 15 行和第 22 行一样自定义 key
  • 15 行表示缓存的 key 为参数 book 对象中 id 的值。
  • 22 行表示缓存的 key 为参数 id
(5)除了上面这种使用参数定义 key 的方式之外,Spring 还提供了一个 root 对象用来生成 key。下面是一些用法实例:
  • #root.methodName:当前方法名
  • #root.method.name:当前方法对象
  • #root.caches[0].name:当前方法使用的缓存
  • #root.target:当前被调用的对象
  • #root.targetClass:当前被调用的对象的 class
  • #root.args[0]:当前方法参数数组
(6)@Cacheput 注解一般用于数据更新方法上:
  • @Cacheable 注解不同,添加了 @Cacheput 注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来。
  • 如果该 key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。
  • 同时,@Cacheput 具有和 @Cacheable 类似的属性,这里不再赘述。
(7)@CacheEvict 注解一般用于删除方法上,表示移除一个 key 对应的缓存。@CacheEvict 注解有两个特殊的属性 allEntries beforelnvocation
  • allEntries 表示是否将所有的缓存数据都移除,默认为 false
  • beforelnvocation 表示是否在方法执行之前移除缓存中的数据,默认为 false,即在方法执行之后移除缓存中的数据。
@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {

    @Cacheable
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }

    @CachePut(key = "#book.id")
    public Book updateBookById(Book book) {
        System.out.println("updateBookById");
        book.setName("三国演义2");
        return book;
    }

    @CacheEvict(key = "#id")
    public void deleteBookById(Integer id) {
        System.out.println("deleteBookById");
    }
}

(3)创建一个测试 ControllerBookDao 中的方法进行测试:
@RestController
public class HelloController {

    @Autowired
    BookDao bookDao;

    @GetMapping("/test")
    public void test() {
        // 连续获取两次数据
        System.out.println("--- 连续获取两次数据 ---");
        bookDao.getBookById(1);
        bookDao.getBookById(1);

        // 删除后再次获取数据
        System.out.println("--- 删除后再次获取数据 ---");
        bookDao.deleteBookById(1);
        Book b3 = bookDao.getBookById(1);
        System.out.println("b3:"+b3);

        // 更新后再次获取数据
        System.out.println("--- 更新后再次获取数据 ---");
        Book b = new Book();
        b.setName("平凡的世界");
        b.setAuthor("路遥");
        b.setId(1);
        bookDao.updateBookById(b);
        Book b4 = bookDao.getBookById(1);
        System.out.println("b4:"+b4);
    }
}

(4)访问这个接口,可以看到控制台打印日志如下:

(2)查看 Redis 服务器的缓存结果,可以发现 Redis 中的 key 都有一个前缀,默认前缀就是“缓存名::

附:自定义缓存 key 的生成器 KeyGenerator

    根据前面介绍可知,默认情况下缓存的 key 是方法的参数。如果不想使用默认的 key,可以使用自定义 key(使用参数定义 key 的方式、或者使用 root 对象来生成 key)
    如果这些 key 不能满足开发需求,我们也可以自定义缓存 key 的生成器 KeyGenerator

1,创建自定义 KeyGenerator

自定义的 MyKeyGenerator 实现 KeyGenerator 接口,然后实现该接口中的 generate 方法。
    generate 方法的三个参数分别是当前对象、当前请求的方法以及方法的参数,开发者可根据这些信息纽成一个新的 key 返回,返回值就是缓存的 key
@Component
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return Arrays.toString(params);
    }
}

2,使用自定义的 KeyGenerator

使用时我们只需要在 @Cacheable 注解中引用 MyKeyGenermor 实例即可。
@Repository
@CacheConfig(cacheNames = "book_cache")
public class BookDao {

    @Autowired
    MyKeyGenerator myKeyGenerator;

    @Cacheable(keyGenerator = "myKeyGenerator")
    public Book getBookById(Integer id) {
        System.out.println("getBookById");
        Book book = new Book();
        book.setId(id);
        book.setName("三国演义");
        book.setAuthor("罗贯中");
        return book;
    }
}
评论

全部评论(0)

回到顶部