Spring Boot 緩存注解@Cacheable、@CachePut、@CacheEvict超詳細(xì)實(shí)戰(zhàn)
?? 前言
在高并發(fā)、高性能的系統(tǒng)開發(fā)中,緩存是提升接口響應(yīng)速度和降低數(shù)據(jù)庫壓力的重要手段。Spring Boot 提供了強(qiáng)大的緩存抽象層 —— spring-context-support,并結(jié)合 JSR-107 標(biāo)準(zhǔn),提供了多個(gè)緩存注解,如:
@Cacheable@CachePut@CacheEvict
這些注解可以讓我們在不侵入業(yè)務(wù)邏輯的前提下,輕松實(shí)現(xiàn)方法級別的緩存管理。
本文將帶你從零開始掌握 Spring Boot 中常用的緩存注解,并通過多個(gè)實(shí)際案例演示其強(qiáng)大功能,包括:
- 查詢緩存優(yōu)化
- 數(shù)據(jù)更新同步
- 刪除緩存策略
- 多級緩存架構(gòu)設(shè)計(jì)
- Redis 實(shí)戰(zhàn)整合
- 緩存穿透、擊穿、雪崩的解決方案
無論你是初學(xué)者,還是有一定經(jīng)驗(yàn)的開發(fā)者,這篇文章都能讓你快速上手 Spring Boot 緩存機(jī)制!
?? 一、Spring Boot 緩存核心機(jī)制概述
? 緩存抽象原理
Spring 的緩存機(jī)制基于 AOP 實(shí)現(xiàn),本質(zhì)上是在方法執(zhí)行前后插入緩存邏輯:
| 注解 | 功能 |
|---|---|
@Cacheable | 先查緩存,有則返回;沒有則執(zhí)行方法并將結(jié)果緩存 |
@CachePut | 執(zhí)行方法后更新緩存(常用于新增或更新操作) |
@CacheEvict | 清除緩存(常用于刪除或刷新緩存) |
?? 注意:
- 這些注解只能用在
public方法上; - 只有當(dāng)調(diào)用方通過 Spring 代理調(diào)用該方法時(shí)才生效(即不能在同一個(gè)類中直接調(diào)用帶注解的方法);
- 需要啟用緩存功能:
@EnableCaching
??? 二、快速入門:開啟緩存支持
Step 1:添加依賴(以 Redis 為例)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>也可以使用本地緩存如 Caffeine 或 Ehcache。
Step 2:啟用緩存
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}?? 三、三大緩存注解詳解與實(shí)戰(zhàn)
? 1. @Cacheable:查詢緩存,避免重復(fù)計(jì)算
場景說明:
當(dāng)你有一個(gè)查詢接口,數(shù)據(jù)變更頻率低,但查詢頻繁,就可以使用 @Cacheable 來緩存結(jié)果。
示例代碼:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
System.out.println("【真實(shí)查詢】用戶ID:" + userId);
return userRepository.findById(userId).orElse(null);
}
}參數(shù)說明:
value/cacheNames:指定緩存名稱,可理解為緩存區(qū)域;key:緩存鍵,默認(rèn)使用參數(shù)生成;unless:條件判斷,例如unless = "#result == null"表示結(jié)果為空時(shí)不緩存;condition:根據(jù)條件決定是否緩存,例如condition = "#userId > 0"。
? 2. @CachePut:更新緩存,保持一致性
場景說明:
當(dāng)你對數(shù)據(jù)進(jìn)行了更新或新增操作,需要同步更新緩存,保證下次查詢拿到的是最新數(shù)據(jù)。
示例代碼:
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}?? 注意:
@CachePut總是會(huì)執(zhí)行方法體,并將結(jié)果放入緩存;- 常用于新增、修改等寫操作。
? 3. @CacheEvict:清除緩存,強(qiáng)制刷新
場景說明:
當(dāng)你刪除某個(gè)數(shù)據(jù)或批量清空緩存時(shí),就需要使用 @CacheEvict。
示例代碼:
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
}清空整個(gè)緩存區(qū):
@CacheEvict(value = "users", allEntries = true)
public void clearAllUsers() {
// 刪除所有用戶邏輯
}?? 參數(shù)說明:
allEntries = true:清空該緩存區(qū)域下的所有鍵;beforeInvocation = true:是否在方法執(zhí)行前清空緩存(默認(rèn) false,在方法執(zhí)行后)。
?? 四、進(jìn)階技巧與最佳實(shí)踐
? 1. 自定義緩存 Key 生成策略
可以通過實(shí)現(xiàn) KeyGenerator 接口自定義緩存 key 的生成方式:
@Bean
public KeyGenerator customKeyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getSimpleName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}然后在注解中使用:
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
? 2. 設(shè)置緩存過期時(shí)間(以 Redis 為例)
在 application.yml 中配置:
spring:
redis:
timeout: 60s
cache:
redis:
time-to-live: 3600000 # 1小時(shí)? 3. 多級緩存策略(Redis + Caffeine)
為了進(jìn)一步提升性能,可以采用“本地緩存 + 分布式緩存”的組合方案:
- 使用
Caffeine作為本地緩存,減少網(wǎng)絡(luò)開銷; - 使用
Redis作為分布式緩存,確保多節(jié)點(diǎn)間數(shù)據(jù)一致。
示例配置:
@Configuration
public class CacheConfig {
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("localUsers");
cacheManager.setCacheBuilder(Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES));
return cacheManager;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}? 4. 緩存穿透、擊穿、雪崩解決方案
| 問題 | 解決方案 |
|---|---|
| 緩存穿透 | 緩存空值、使用布隆過濾器 |
| 緩存擊穿 | 加互斥鎖、設(shè)置永不過期 |
| 緩存雪崩 | 設(shè)置不同過期時(shí)間、集群緩存 |
示例:防止緩存穿透
@Cacheable(value = "products", key = "#productId", unless = "#result == null")
public Product getProductById(Long productId) {
Product product = productRepository.findById(productId).orElse(null);
if (product == null) {
// 緩存空值,防止緩存穿透
redisService.setWithExpire("product:" + productId, "", 60, TimeUnit.SECONDS);
}
return product;
}?? 五、綜合案例:商品信息緩存實(shí)戰(zhàn)
背景:
我們有一個(gè)商品服務(wù),包含如下功能:
- 查詢商品詳情(緩存)
- 更新商品信息(更新緩存)
- 刪除商品(清除緩存)
代碼示例:
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Cacheable(value = "products", key = "#productId")
public Product getProductById(Long productId) {
return productRepository.findById(productId).orElse(null);
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
@CacheEvict(value = "products", key = "#productId")
public void deleteProduct(Long productId) {
productRepository.deleteById(productId);
}
}?? 六、常見問題與解決方案
| 問題 | 原因 | 解決辦法 |
|---|---|---|
| 注解不生效 | 沒加 @EnableCaching | 添加注解 |
| 同一類內(nèi)調(diào)用失效 | AOP 代理失效 | 將方法抽離到另一個(gè) Bean 中 |
| 緩存未更新 | 沒使用 @CachePut 或 @CacheEvict | 加上對應(yīng)注解 |
| key 沖突 | key 重復(fù)導(dǎo)致覆蓋 | 使用更細(xì)粒度的 key 策略 |
| 不支持序列化 | 緩存對象未實(shí)現(xiàn) Serializable | 使用 JSON 序列化或?qū)崿F(xiàn)接口 |
?? 七、總結(jié)對比表
| 注解 | 作用 | 是否執(zhí)行方法 | 適用場景 |
|---|---|---|---|
@Cacheable | 查詢緩存 | 有緩存則跳過方法 | 查詢頻繁、更新少的數(shù)據(jù) |
@CachePut | 更新緩存 | 總是執(zhí)行方法 | 新增/修改操作 |
@CacheEvict | 刪除緩存 | 總是執(zhí)行方法 | 刪除或刷新緩存 |
?? 八、結(jié)語
Spring Boot 的緩存注解為我們提供了一種聲明式的緩存管理方式,極大地提升了開發(fā)效率和系統(tǒng)性能。無論是構(gòu)建企業(yè)級后臺(tái)系統(tǒng),還是搭建高并發(fā) API 平臺(tái),都應(yīng)該合理使用緩存來優(yōu)化系統(tǒng)表現(xiàn)。
通過本文的學(xué)習(xí),你已經(jīng)掌握了:
- 如何使用
@Cacheable、@CachePut、@CacheEvict; - 如何結(jié)合 Redis 或本地緩存進(jìn)行實(shí)際應(yīng)用;
- 如何避免緩存穿透、雪崩、擊穿等問題;
- 如何提高系統(tǒng)的響應(yīng)速度和穩(wěn)定性。
到此這篇關(guān)于Spring Boot 緩存注解詳解:@Cacheable、@CachePut、@CacheEvict(超詳細(xì)實(shí)戰(zhàn)版)的文章就介紹到這了,更多相關(guān)Spring Boot @Cacheable、@CachePut、@CacheEvict內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合Redisson的步驟(單機(jī)版)
Redisson非常適用于分布式鎖,而我們的一項(xiàng)業(yè)務(wù)需要考慮分布式鎖這個(gè)應(yīng)用場景,于是我整合它做一個(gè)初步簡單的例子(和整合redis一樣)。2021-05-05
java圖形驗(yàn)證碼生成工具類 web頁面校驗(yàn)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了java圖形驗(yàn)證碼生成工具類,web頁面校驗(yàn)驗(yàn)證碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Java 實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲框架詳細(xì)代碼
這篇文章主要介紹了Java 實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲框架,主要是用于爬取網(wǎng)絡(luò)上一些內(nèi)容,比如超鏈接之類的,需要的朋友可以參考下面文章內(nèi)容2021-09-09
java實(shí)現(xiàn)圖片的上傳與展示實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)圖片的上傳與展示的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12
SpringBoot集成SpirePDF實(shí)現(xiàn)文本替換功能
SpirePDF是一個(gè)用于.NET平臺(tái)的高級PDF文檔處理庫,它提供了一套完整的API,允許開發(fā)者創(chuàng)建、編輯、轉(zhuǎn)換、合并、分割和解析PDF文件本文給大家介紹了SpringBoot集成SpirePDF實(shí)現(xiàn)文本替換功能,需要的朋友可以參考下2024-09-09

