1. 簡介
在本教程中,我們將學習如何使用 Spring 中的緩存抽象,並通常提高我們系統的性能。
我們將啓用簡單的緩存機制,用於一些實際方法示例,並通過智能緩存管理來有效地提高這些調用的性能。
2. 快速入門
在開始實施緩存之前,我們需要確保我們的 Spring Boot 應用程序具有必要的依賴項,以便支持緩存。
2.1. 使用 Spring Boot
要開始在 Spring Boot 應用程序中緩存,Spring 提供了一個易於集成的緩存抽象,可以使用 spring-boot-starter-cache starter 包輕鬆集成:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>3.5.7</version>
</dependency>在底層,啓動器引入了 spring-context-support 模塊。
2.2. 使用 Spring Core
Spring Core 核心緩存抽象位於 spring-context 模塊中。因此,在使用 Maven 時,我們的 pom.xml 應包含以下依賴項:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.11</version>
</dependency>有趣的是,還有一個名為 spring-context-support 的模塊,它位於 spring-context 模塊之上,並提供了一些由 EhCache 或 Caffeine 等技術支持的 CacheManagers。 如果我們想使用它們作為我們的緩存存儲,則需要使用 spring-context-support 模塊:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>6.2.11</version>
</dependency>由於 spring-context-support 模塊間接依賴 spring-context 模塊,因此無需為 spring-context 模塊單獨聲明依賴。
3. 啓用緩存
要啓用 Spring 中的緩存,我們可以利用註解,類似於我們在框架內激活其他配置級別功能的方式。
在 Spring Boot 應用程序中,類路徑上僅存在 starter 包以及 EnableCaching 註解,就足以註冊相同的 ConcurrentMapCacheManager。
此外,我們還可以使用一個或多個 CacheManagerCustomizer<T> Bean 來自定義 自動配置 CacheManager。
@Component
public class SimpleCacheCustomizer
implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(asList("users", "transactions"));
}
}《CacheAutoConfiguration》自動配置會拾取這些自定義配置器,並在其完全初始化之前將其應用於當前的 CacheManager。
我們可以通過在任何配置類中添加 @EnableCaching 註解來啓用緩存功能:
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("addresses");
}
}注意:在啓用緩存後,為了最小化配置,必須註冊一個 cacheManager。
4. 使用緩存與註釋
一旦我們啓用了緩存,下一步就是將緩存行為綁定到帶有聲明式註解的方法上。
4.1. @Cacheable
要啓用方法緩存行為的最簡單方法是使用 @Cacheable 標記該方法,並使用緩存名稱來參數化該方法:
@Cacheable("addresses")
public String getAddress(Customer customer) {...}
調用 getAddress() 方法首先會檢查緩存 addresses,然後在真正調用方法之前緩存結果,並緩存結果。
雖然在大多數情況下,一個緩存就足夠了,但 Spring 框架也支持將多個緩存作為參數傳遞:
@Cacheable({"addresses", "directory"})
public String getAddress(Customer customer) {...}在這種情況下,如果任何緩存都包含所需的結果,則結果將被返回,並且該方法不會被調用。
4.2. <em @CacheEvict</em>>
現在,如果將所有方法都標記為 <em @Cacheable</em>>,會產生什麼問題呢?
問題在於大小。我們不想用那些不經常使用的值填充緩存。緩存可能會變得非常大,而且速度很快,我們可能會持有大量過時或未使用的數據。
我們可以使用 <em @CacheEvict</em>> 註解來指示刪除一個或多個/所有值,以便重新加載新鮮的值到緩存中:
@CacheEvict(value="addresses", allEntries=true)
public String getAddress(Customer customer) {...}我們在這裏使用額外的參數 allEntries 與緩存一起用於清空緩存;這將清除緩存中的所有 addresses 條目,併為新數據做準備。
4.3. @CachePut
While @CacheEvict reduces the overhead of looking up entries in a large cache by removing stale and unused entries, we want to 避免過多地從緩存中移除數據。
Instead, we selectively update the entries whenever we alter them.
With the @CachePut annotation, we can update the content of the cache without interfering with the method execution. That is, the method will always be executed and the result cached:
@CachePut(value="addresses")
public String getAddress(Customer customer) {...}<p><em>@Cacheable</em> 和 <em>@CachePut</em> 的區別在於,<em>@Cacheable</em> 會 <strong >跳過執行方法</strong>,而 <em>@CachePut</em> 會 <strong >實際執行方法</strong>,然後將結果放入緩存。</p>
4.4. 緩存 (@Caching)
如果我們要為緩存方法使用相同類型的多個註解,請看以下一個錯誤的例子:
@CacheEvict("addresses")
@CacheEvict(value="directory", key=customer.name)
public String getAddress(Customer customer) {...}上述代碼由於 Java 不允許為同一方法聲明相同類型的多個註解而編譯失敗。
解決上述問題的辦法是:
@Caching(evict = {
@CacheEvict("addresses"),
@CacheEvict(value="directory", key="#customer.name") })
public String getAddress(Customer customer) {...}如代碼片段所示,我們可以使用 多重緩存註解,並通過 @Caching 實現自定義緩存邏輯。
4.5. @CacheConfig
通過使用 <em/>@CacheConfig</em/> 註解,我們可以將一些緩存配置集中到類級別,從而避免在多個地方重複聲明:
@CacheConfig("addresses")
public class CustomerDataService {
@Cacheable
public String getAddress(Customer customer) {...}
// ...
}5. 條件緩存
有時,緩存可能無法很好地適用於所有方法的情況。
再次使用我們之前在 @CachePut 註解中的示例,它將每次都執行方法並緩存結果:
@CachePut(value="addresses")
public String getAddress(Customer customer) {...}
5.1. 條件參數
如果想要對標註的激活時機進行更精細的控制,我們可以使用條件參數來參數化 <em >@CachePut</em>,該參數接受一個 SpEL 表達式,並確保緩存結果基於評估該表達式進行:
@CachePut(value="addresses", condition="#customer.name=='Tom'")
public String getAddress(Customer customer) {...}5.2. 未指定參數
我們可以通過 unless 參數,根據方法的輸出而非輸入來控制緩存行為:
@CachePut(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}上述註釋將緩存地址,除非它們長度小於 64 個字符。
需要注意的是,condition 和 unless 參數可以與所有緩存註釋一起使用。
這種條件緩存對於管理大型結果非常有效。它還可用於根據輸入參數自定義行為,而不是強制所有操作都採用通用行為。
6. 基於Java的緩存
以下是等效的Java配置:
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("directory"),
new ConcurrentMapCache("addresses")));
return cacheManager;
}
}以下是我們的 CustomerDataService:
@Component
public class CustomerDataService {
@Cacheable(value = "addresses", key = "#customer.name")
public String getAddress(Customer customer) {
return customer.getAddress();
}
}7. 結論
在本文中,我們探討了 Spring 緩存的基礎知識,重點介紹了諸如 <em @Cacheable</em>, <em @CacheEvict</em>, 和 <em @CachePut</em> 等註解。 通過明智地使用緩存,我們可以顯著提高應用程序的性能,尤其是在存在重複數據檢索或昂貴方法調用領域。