优雅使用Spring Cache缓存

什么场景选择Spring Cache

在做技术选型的时候,需要针对场景选择不同的技术。

笔者认为Spring Cache的功能很强大,设计也非常优雅。特别适合缓存控制没有那么细致的场景。比如门户首页,偏静态展示页面,榜单等等。

这些场景的特点是对数据实时性没有那么严格的要求,只需要将数据源缓存下来,过期之后自动刷新即可。这些场景下,Spring Cache就是神器,能大幅度提升研发效率。

首先需要明确一点:Spring Cache不是一个具体的缓存实现方案,而是一个对缓存使用的抽象(Cache Abstraction )。

一.缓存声明

缓存声明,也就是标识需要缓存的方法以及缓存策略

Spring Cache 提供了五个注解。

  1. @Cacheable:将方法的结果缓存起来,当使用相同的参数再次调用该方法时,会直接从缓存中获取结果,而不再执行方法体。适用于读取频繁但耗时较长的方法。

  2. @CachePut:每次调用方法都会触发真实方法的执行,并将返回结果缓存起来。适用于更新或插入数据后需要刷新缓存的场景。

  3. @CacheEvict:根据一定的条件删除缓存,可以在方法执行前、执行后或异常时进行缓存清除操作。适用于需要手动清除缓存的情况。

  4. @Caching:用于组合多个缓存相关的注解,可以在一个方法中同时使用多个缓存注解。

  5. @CacheConfig:在类级别上配置共享的缓存相关配置,可以指定缓存的名称、过期时间等。

我们重点讲解:@Cacheable,@CachePut,@CacheEvict三个核心注解。

1.1 @Cacheable注解

@Cacheble注解表示这个方法有了缓存的功能。

@Cacheable(value="user_cache",key="#userId", unless="#result == null") 
public User getUserById(Long userId) {
  User user = userMapper.getUserById(userId); return user;
}

 

上面的代码片段里,getUserById方法和缓存user_cache 关联起来,若方法返回的User对象不为空,则缓存起来。

第二次相同参数userId调用该方法的时候,直接从缓存中获取数据,并返回。

1.2 @CachePut注解

@CachePut注解作用于缓存需要被更新的场景,和 @Cacheable 非常相似,但被注解的方法每次都会被执行。

返回值是否会放入缓存,依赖于condition和unless,默认情况下结果会存储到缓存。

@CachePut(value = "user_cache", key="#user.id", unless = "#result != null") 
public User updateUser(User user) {
  userMapper.updateUser(user);
  return user;
}

 

当调用updateUser方法时,每次方法都会被执行,但是因为unless属性每次都是true,所以并没有将结果缓存。当去掉unless属性,则结果会被缓存。

1.3 @CacheEvict注解

@CacheEvict 注解的方法在调用时会从缓存中移除已存储的数据。

@CacheEvict(value = "user_cache", key = "#id") 
public void deleteUserById(Long id) {
  userMapper.deleteUserById(id);
}

 

当调用deleteUserById方法完成后,缓存key等于参数id的缓存会被删除,而且方法的返回的类型是Void ,这和@Cacheable明显不同。

二.入门示例

复制代码
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
 </dependency>
 <dependency>
   <groupId>com.github.ben-manes.caffeine</groupId>
   <artifactId>caffeine</artifactId>
   <version>2.7.0</version>
 </dependency>
复制代码

2.1.2 Caffeine缓存配置

我们先创建一个缓存配置类MyCacheConfig。

复制代码
@Configuration
 @EnableCaching
 public class MyCacheConfig {
   @Bean
   public Caffeine caffeineConfig() {
     return
       Caffeine.newBuilder()
       .maximumSize(10000).
       expireAfterWrite(60, TimeUnit.MINUTES);
   }
   @Bean
   public CacheManager cacheManager(Caffeine caffeine) {
     CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
     caffeineCacheManager.setCaffeine(caffeine);
     return caffeineCacheManager;
   }
 }
复制代码

首先创建了一个Caffeine对象,该对象标识本地缓存的最大数量是10000条,每个缓存数据在写入60分钟后失效。

另外,MyCacheConfig类上我们添加了注解:**@EnableCaching** 。

2.1.3  业务代码

根据缓存声明 这一节,我们很容易写出如下代码。

复制代码
@Cacheable(value = "user_cache", unless = "#result == null")
 public User getUserById(Long id) {
     return userMapper.getUserById(id);
 }
 @CachePut(value = "user_cache", key = "#user.id", unless = "#result == null")
 public User updateUser(User user) {
     userMapper.updateUser(user);
     return user;
 }
 @CacheEvict(value = "user_cache", key = "#id")
 public void deleteUserById(Long id) {
     userMapper.deleteUserById(id);
 }
复制代码

当我们在Controller层调用 getUserById方法时,调试的时候,配置mybatis日志级别为DEBUG,方便监控方法是否会缓存。

第一次调用会查询数据库,打印相关日志:

Preparing: select * FROM user t where t.id = ? Parameters: 1(Long) Total: 1

第二次调用查询方法的时候,数据库SQL日志就没有出现了, 也就说明缓存生效了。

2.2 集成Redisson

<dependency>
    <groupId>org.Redisson</groupId>
    <artifactId>Redisson</artifactId>
    <version>3.12.0</version>
 </dependency>
复制代码
@Bean(destroyMethod = "shutdown")
public RedissonClient Redisson(){ 
  Config config = new Config(); 
  config.useSingleServer() .setAddress("redis://127.0.0.1:6201").setPassword("ts112GpO_ay");
  config.setCodec(new JsonJacksonCodec());
  return Redisson.create(config);
}
@Bean 
CacheManager cacheManager(RedissonClient RedissonClient) {
  Map
<String, CacheConfig> config = new HashMap<String, CacheConfig>();
  // create "user_cache" spring cache with ttl = 24 minutes and maxIdleTime = 12 minutes
  config.put("user_cache", new CacheConfig( 24 * 60 * 1000, 12 * 60 * 1000));
  return new RedissonSpringCacheManager(RedissonClient, config);
}
复制代码

可以看到,从Caffeine切换到Redisson,只需要修改缓存配置类,定义CacheManager 对象即可。而业务代码并不需要改动。

-----------------------------------------------------------
上一章 Java不会用缓存-那就手写吧

作者:风浪很小
-----------------------------------------------------------
posted @   风浪很小  阅读(437)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示