优雅使用Spring Cache缓存
什么场景选择Spring Cache
在做技术选型的时候,需要针对场景选择不同的技术。
笔者认为Spring Cache的功能很强大,设计也非常优雅。特别适合缓存控制没有那么细致的场景。比如门户首页,偏静态展示页面,榜单等等。
这些场景的特点是对数据实时性没有那么严格的要求,只需要将数据源缓存下来,过期之后自动刷新即可。这些场景下,Spring Cache就是神器,能大幅度提升研发效率。
首先需要明确一点:Spring Cache不是一个具体的缓存实现方案,而是一个对缓存使用的抽象(Cache Abstraction )。
一.缓存声明
缓存声明,也就是标识需要缓存的方法以及缓存策略 。
Spring Cache 提供了五个注解。
-
@Cacheable
:将方法的结果缓存起来,当使用相同的参数再次调用该方法时,会直接从缓存中获取结果,而不再执行方法体。适用于读取频繁但耗时较长的方法。 -
@CachePut
:每次调用方法都会触发真实方法的执行,并将返回结果缓存起来。适用于更新或插入数据后需要刷新缓存的场景。 -
@CacheEvict
:根据一定的条件删除缓存,可以在方法执行前、执行后或异常时进行缓存清除操作。适用于需要手动清除缓存的情况。 -
@Caching
:用于组合多个缓存相关的注解,可以在一个方法中同时使用多个缓存注解。 -
@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不会用缓存-那就手写吧
-----------------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)