快取抽象:JCache (JSR-107) 註解支援

工程 | Stéphane Nicoll | 2014年4月14日 | ...

Spring 的快取抽象 從 Spring 3.1 開始就已可用,是時候給予它更多的關注了。在這篇文章中,我想帶您瞭解該領域的主要改進,即 JCache (JSR-107) 註解支援。

您可能聽說過,JSR-107 最終確定了,距離最初的提案已經過去了 13 年。對於熟悉 Spring 快取註解的人來說,下表描述了 Spring 註解與 JSR-107 對應註解之間的對映關係。

Spring JSR-107
@Cacheable @CacheResult
@CachePut @CachePut
@CacheEvict @CacheRemove
@CacheEvict(allEntries=true) @CacheRemoveAll

JCache 註解

讓我們先來看每個註解以及它們的使用方法。這將是一個更好地理解它們支援您已經熟悉的 Spring 註解以及更重要的是這些註解帶來的 **新** 功能的機會。

@CacheResult

@CacheResult@Cacheable 非常相似,下面的示例使用 @CacheResult 註解重寫了原始示例

@CacheResult(cacheName = "books")
public Book findBook(ISBN isbn) {...}

可以使用 CacheKeyGenerator 介面自定義鍵的生成。如果未指定特定實現,則根據規範,預設實現會使用所有引數,除非一個或多個引數被 @CacheKey 註解標記,在這種情況下,只使用這些引數。假設上面的方法現在需要一個不應包含在鍵中的額外屬性,以下是使用 JCache 實現它的方法。

@CacheResult(cacheName = "book")
public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse) { ... }

@CacheResult 引入了“異常快取”的概念:每當方法執行失敗時,就可以**快取**丟擲的異常,以防止再次呼叫該方法。假設如果 ISBN 結構無效,則會丟擲 InvalidIsbnNotFoundException。這是一個永久性故障,永遠無法使用該引數檢索到書籍。以下程式碼將異常快取起來,以便將來使用相同的無效 ISBN 進行呼叫時,直接丟擲快取的異常,而無需再次呼叫該方法。

@CacheResult(cacheName = "books", exceptionCacheName = "failures"
             cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(@CacheKey ISBN isbn) { ... }

當然,盲目地丟擲快取的異常可能會非常令人困惑,因為呼叫堆疊可能與當前的呼叫上下文不匹配。我們盡最大努力透過複製具有一致呼叫堆疊的異常來確保堆疊跟蹤匹配。

JCache 具有 CacheResolver 的強大功能,可以允許在執行時解析要使用的快取。由於 JCache 支援常規快取和異常快取,因此要使用的 CacheResolver 例項由 CacheResolverFactory 決定。明顯的預設行為是根據 cacheNameexceptionCacheName 屬性分別解析要使用的快取。但是,也可以為每個操作自定義要使用的工廠。

@CacheResult(cacheName = "books", cacheResolverFactory = MyFactory.class)
public Book findBook(@CacheKey ISBN isbn) { ... }

最後,@CacheResult 有一個 skipGet 屬性,可以啟用該屬性以**始終**呼叫方法,無論快取狀態如何。這實際上與我們使用 @CachePut 的方式非常相似。

@CachePut

雖然註解名稱相同,但 JCache 中的語義卻大不相同。對我們的書籍進行簡單更新將如下編寫:

@CachePut(value = "books", key = "#p0")
public Book update(ISBN isbn, Book updatedBook) { ... }

而 JCache 會要求您這樣寫:

@CachePut(cacheName = "books")
public void update(ISBN isbn, @CacheValue Book updatedBook) { ... }

請注意,儘管 updatedBook 不應是鍵的一部分,但我們不必為第一個引數新增 @CacheKey。這是因為帶有 @CacheValue 註解的引數會自動從鍵生成中排除。

@CacheResult 一樣,@CachePut 允許管理執行方法時丟擲的任何異常,如果丟擲的異常與註解上指定的過濾器匹配,則阻止 put 操作的發生。

最後,可以控制快取是在呼叫帶註解的方法之前還是之後更新。當然,如果在此之前更新,則不會進行異常處理。

@CacheRemove 和 @CacheRemoveAll

這些與 @CacheEvict@CacheEvict(allEntries = true) 分別非常相似。@CacheRemove 具有特殊的異常處理機制,可以防止在帶註解的方法丟擲與註解上指定的過濾器匹配的異常時進行失效。

其他功能

CacheDefaults

@CacheDefaults 是一個類級別的註解,它允許您**共享**類上定義的任何快取操作的通用設定。這些設定包括:

  • 快取的名稱
  • 自定義的 CacheResolverFactory
  • 自定義的 CacheKeyGenerator

在下面的示例中,任何與快取相關的操作都將使用 books 快取。

@CacheDefaults(cacheName = "books")
public class BookRepositoryImpl implements BookRepository {

    @CacheResult
    public Book findBook(@CacheKey ISBN isbn) { ... }
}

啟用 JSR-107 支援

JCache 支援的實現使用了我們自己的 CacheCacheManager 抽象,這意味著您可以使用現有的 CacheManager 基礎設施,同時仍然使用標準註解!

要啟用 Spring 快取註解的支援,您通常會使用 @EnableCaching<cache:annotation-driven/> XML 元素,例如:

@Configuration
@EnableCaching
public class AppConfig {
    @Bean
    public CacheManager cacheManager() { ...}

    ...
}

那麼,要將標準註解的支援引入混合使用需要什麼呢?不多。只需在類路徑中新增 JCache API 和 spring-context-support 模組(如果尚未新增),您就已準備就緒。

現有的基礎設施實際上會檢查 JCache API 的存在情況,當發現 JCache API 並與 Spring 的 JCache 支援一起存在時,它還將配置必要的基礎設施來支援標準註解。

# 總結

長話短說,如果您已經在使用的 Spring 快取抽象,並且想嘗試標準註解,只需在您的專案中新增另外兩個依賴項即可開始。

想試用一下嗎?獲取 Spring 4.1 的夜間 SNAPSHOT 版本,並將 javax.cache:cache-api:1.0.0org.springframework:spring-context-support:4.1.0.BUILD-SNAPSHOT 依賴項新增到您的專案中。如果您需要更多詳細資訊,文件也已更新。

在下一篇文章中,我將介紹支援 JSR-107 註解如何影響我們自己的支援以及其他一些與快取相關的改進。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有