快取

Spring Framework 提供了透明地將快取新增至應用程式的支援。 其核心抽象概念將快取應用於方法,從而根據快取中可用的資訊減少執行次數。 快取邏輯是透明地應用的,不會干擾調用者。 只要使用 @EnableCaching 註解啟用快取支援,Spring Boot 就會自動組態快取基礎架構。

請查看 Spring Framework 參考文件的相關章節以取得更多詳細資訊。

簡而言之,若要將快取新增至服務的操作,請將相關註解新增至其方法,如下列範例所示

  • Java

  • Kotlin

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MyMathService {

	@Cacheable("piDecimals")
	public int computePiDecimal(int precision) {
		...
	}

}
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Component

@Component
class MyMathService {

	@Cacheable("piDecimals")
	fun computePiDecimal(precision: Int): Int {
		...
	}

}

此範例示範了在潛在的昂貴操作上使用快取。 在調用 computePiDecimal 之前,抽象概念會在 piDecimals 快取中尋找與 precision 引數相符的項目。 如果找到項目,快取中的內容會立即傳回給調用者,而不會調用該方法。 否則,將調用該方法,並在傳回值之前更新快取。

您也可以透明地使用標準 JSR-107 (JCache) 註解 (例如 @CacheResult)。 但是,我們強烈建議您不要混合搭配 Spring Cache 和 JCache 註解。

如果您未新增任何特定的快取庫,Spring Boot 會自動組態一個簡單提供者,其在記憶體中使用並行映射。 當需要快取時 (例如先前範例中的 piDecimals),此提供者會為您建立它。 簡單提供者實際上不建議用於生產環境,但它非常適合入門並確保您了解這些功能。 當您決定要使用的快取提供者後,請務必閱讀其文件,以了解如何組態您的應用程式使用的快取。 幾乎所有提供者都要求您明確組態應用程式中使用的每個快取。 有些提供一種方法來自訂 spring.cache.cache-names 屬性定義的預設快取。

也可以透明地從快取更新逐出資料。

支援的快取提供者

快取抽象概念不提供實際的儲存,而是依賴於 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 介面實現的抽象概念。

如果您尚未定義類型為 CacheManager 的 bean 或名為 cacheResolverCacheResolver (請參閱 CachingConfigurer),Spring Boot 會嘗試偵測下列提供者 (依指示的順序)

  1. 通用

  2. JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan 等)

  3. Hazelcast

  4. Infinispan

  5. Couchbase

  6. Redis

  7. Caffeine

  8. Cache2k

  9. 簡單

如果 CacheManager 由 Spring Boot 自動組態,則可以透過設定 spring.cache.type 屬性來強制使用特定的快取提供者。 如果您需要在某些環境 (例如測試) 中使用 no-op 快取,請使用此屬性。
使用 `spring-boot-starter-cache` 啟動器快速新增基本快取依賴。 此啟動器引入了 `spring-context-support`。 如果您手動新增依賴,則必須包含 `spring-context-support` 才能使用 JCache 或 Caffeine 支援。

如果 CacheManager 由 Spring Boot 自動組態,您可以在完全初始化之前,透過公開實作 `CacheManagerCustomizer` 介面的 bean 來進一步調整其組態。 下列範例設定一個旗標,表示不應將 null 值傳遞到基礎映射

  • Java

  • Kotlin

import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyCacheManagerConfiguration {

	@Bean
	public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
		return (cacheManager) -> cacheManager.setAllowNullValues(false);
	}

}
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer
import org.springframework.cache.concurrent.ConcurrentMapCacheManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyCacheManagerConfiguration {

	@Bean
	fun cacheManagerCustomizer(): CacheManagerCustomizer<ConcurrentMapCacheManager> {
		return CacheManagerCustomizer { cacheManager ->
			cacheManager.isAllowNullValues = false
		}
	}

}
在前面的範例中,預期會自動組態 `ConcurrentMapCacheManager`。 如果情況並非如此 (您提供了自己的組態或自動組態了不同的快取提供者),則完全不會調用自訂器。 您可以擁有任意數量的自訂器,也可以使用 `@Order` 或 `Ordered` 對它們進行排序。

通用

如果內容至少定義一個 `org.springframework.cache.Cache` bean,則會使用通用快取。 建立包裝該類型所有 bean 的 `CacheManager`。

JCache (JSR-107)

JCache 是透過類別路徑上存在 `javax.cache.spi.CachingProvider` 來引導的 (也就是說,類別路徑上存在符合 JSR-107 標準的快取庫),而 `JCacheCacheManager` 由 `spring-boot-starter-cache` 啟動器提供。 有各種符合標準的庫可用,Spring Boot 為 Ehcache 3、Hazelcast 和 Infinispan 提供依賴管理。 也可以新增任何其他符合標準的庫。

可能會發生存在多個提供者的情況,在這種情況下,必須明確指定提供者。 即使 JSR-107 標準沒有強制規定定義組態檔位置的標準化方法,Spring Boot 仍會盡力容納使用實作詳細資訊設定快取,如下列範例所示

  • 屬性

  • YAML

spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml
# Only necessary if more than one provider is present
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
當快取庫同時提供原生實作和 JSR-107 支援時,Spring Boot 會優先選擇 JSR-107 支援,以便在您切換到不同的 JSR-107 實作時,相同的特性仍然可用。
Spring Boot 一般支援 Hazelcast。 如果單個 `HazelcastInstance` 可用,它也會自動重複用於 `CacheManager`,除非指定了 `spring.cache.jcache.config` 屬性。

有兩種方法可以自訂基礎的 `javax.cache.cacheManager`

  • 可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立快取。 如果定義了自訂 `javax.cache.configuration.Configuration` bean,則會使用它來自訂它們。

  • `org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer` bean 會以 `CacheManager` 的參考調用,以進行完整自訂。

如果定義了標準 `javax.cache.CacheManager` bean,它會自動包裝在抽象概念預期的 `org.springframework.cache.CacheManager` 實作中。 不會對其應用進一步的自訂。

Hazelcast

Spring Boot 一般支援 Hazelcast。 如果已自動組態 `HazelcastInstance` 且 `com.hazelcast:hazelcast-spring` 在類別路徑上,則會自動將其包裝在 `CacheManager` 中。

Hazelcast 可以用作符合 JCache 標準的快取,也可以用作符合 Spring `CacheManager` 標準的快取。 當將 `spring.cache.type` 設定為 `hazelcast` 時,Spring Boot 將使用基於 `CacheManager` 的實作。 如果您想將 Hazelcast 用作符合 JCache 標準的快取,請將 `spring.cache.type` 設定為 `jcache`。 如果您有多個符合 JCache 標準的快取提供者,並且想要強制使用 Hazelcast,則必須明確設定 JCache 提供者

Infinispan

Infinispan 沒有預設組態檔位置,因此必須明確指定。 否則,將使用預設引導。

  • 屬性

  • YAML

spring.cache.infinispan.config=infinispan.xml
spring:
  cache:
    infinispan:
      config: "infinispan.xml"

可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立快取。 如果定義了自訂 `ConfigurationBuilder` bean,則會使用它來自訂快取。

為了與 Spring Boot 的 Jakarta EE 9 基準相容,必須使用 Infinispan 的 `-jakarta` 模組。 對於每個具有 `-jakarta` 變體的模組,都必須使用該變體來取代標準模組。 例如,必須使用 `infinispan-core-jakarta` 和 `infinispan-commons-jakarta` 來取代 `infinispan-core` 和 `infinispan-commons`。

Couchbase

如果 Spring Data Couchbase 可用且已組態 Couchbase,則會自動組態 `CouchbaseCacheManager`。 可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立其他快取,並且可以使用 `spring.cache.couchbase.*` 屬性來組態快取預設值。 例如,下列組態會建立 `cache1` 和 `cache2` 快取,其項目到期時間為 10 分鐘

  • 屬性

  • YAML

spring.cache.cache-names=cache1,cache2
spring.cache.couchbase.expiration=10m
spring:
  cache:
    cache-names: "cache1,cache2"
    couchbase:
      expiration: "10m"

如果您需要更精細地控制組態,請考慮註冊 `CouchbaseCacheManagerBuilderCustomizer` bean。 下列範例顯示了一個自訂器,該自訂器為 `cache1` 和 `cache2` 組態了特定的項目到期時間

  • Java

  • Kotlin

import java.time.Duration;

import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;

@Configuration(proxyBeanMethods = false)
public class MyCouchbaseCacheManagerConfiguration {

	@Bean
	public CouchbaseCacheManagerBuilderCustomizer myCouchbaseCacheManagerBuilderCustomizer() {
		return (builder) -> builder
				.withCacheConfiguration("cache1", CouchbaseCacheConfiguration
						.defaultCacheConfig().entryExpiry(Duration.ofSeconds(10)))
				.withCacheConfiguration("cache2", CouchbaseCacheConfiguration
						.defaultCacheConfig().entryExpiry(Duration.ofMinutes(1)));

	}

}
import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyCouchbaseCacheManagerConfiguration {

	@Bean
	fun myCouchbaseCacheManagerBuilderCustomizer(): CouchbaseCacheManagerBuilderCustomizer {
		return CouchbaseCacheManagerBuilderCustomizer { builder ->
			builder
				.withCacheConfiguration(
					"cache1", CouchbaseCacheConfiguration
						.defaultCacheConfig().entryExpiry(Duration.ofSeconds(10))
				)
				.withCacheConfiguration(
					"cache2", CouchbaseCacheConfiguration
						.defaultCacheConfig().entryExpiry(Duration.ofMinutes(1))
				)
		}
	}

}

Redis

如果 Redis 可用且已組態,則會自動組態 `RedisCacheManager`。 可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立其他快取,並且可以使用 `spring.cache.redis.*` 屬性來組態快取預設值。 例如,下列組態會建立 `cache1` 和 `cache2` 快取,其存活時間為 10 分鐘

  • 屬性

  • YAML

spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=10m
spring:
  cache:
    cache-names: "cache1,cache2"
    redis:
      time-to-live: "10m"
預設情況下,會新增金鑰前綴,以便在兩個不同的快取使用相同的金鑰時,Redis 不會出現金鑰重疊的情況,並且無法傳回無效值。 如果您建立自己的 `RedisCacheManager`,我們強烈建議您保持啟用此設定。
您可以透過新增您自己的 `RedisCacheConfiguration` `@Bean` 來完全控制預設組態。 如果您需要自訂預設序列化策略,這會很有用。

如果您需要更精細地控制組態,請考慮註冊 `RedisCacheManagerBuilderCustomizer` bean。 下列範例顯示了一個自訂器,該自訂器為 `cache1` 和 `cache2` 組態了特定的存活時間

  • Java

  • Kotlin

import java.time.Duration;

import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;

@Configuration(proxyBeanMethods = false)
public class MyRedisCacheManagerConfiguration {

	@Bean
	public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
		return (builder) -> builder
				.withCacheConfiguration("cache1", RedisCacheConfiguration
						.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)))
				.withCacheConfiguration("cache2", RedisCacheConfiguration
						.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)));

	}

}
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyRedisCacheManagerConfiguration {

	@Bean
	fun myRedisCacheManagerBuilderCustomizer(): RedisCacheManagerBuilderCustomizer {
		return RedisCacheManagerBuilderCustomizer { builder ->
			builder
				.withCacheConfiguration(
					"cache1", RedisCacheConfiguration
						.defaultCacheConfig().entryTtl(Duration.ofSeconds(10))
				)
				.withCacheConfiguration(
					"cache2", RedisCacheConfiguration
						.defaultCacheConfig().entryTtl(Duration.ofMinutes(1))
				)
		}
	}

}

Caffeine

Caffeine 是 Guava 快取的 Java 8 重寫版本,它取代了對 Guava 的支援。 如果 Caffeine 存在,則會自動組態 `CaffeineCacheManager` (由 `spring-boot-starter-cache` 啟動器提供)。 可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立快取,並且可以透過下列其中一種方式自訂 (依指示的順序)

  1. 由 `spring.cache.caffeine.spec` 定義的快取規格

  2. 已定義 `com.github.benmanes.caffeine.cache.CaffeineSpec` bean

  3. 已定義 `com.github.benmanes.caffeine.cache.Caffeine` bean

例如,下列組態會建立 `cache1` 和 `cache2` 快取,其最大大小為 500,存活時間為 10 分鐘

  • 屬性

  • YAML

spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
spring:
  cache:
    cache-names: "cache1,cache2"
    caffeine:
      spec: "maximumSize=500,expireAfterAccess=600s"

如果定義了 `com.github.benmanes.caffeine.cache.CacheLoader` bean,則會自動將其與 `CaffeineCacheManager` 關聯。 由於 `CacheLoader` 將與快取管理器管理的所有快取關聯,因此必須將其定義為 `CacheLoader<Object, Object>`。 自動組態會忽略任何其他泛型類型。

Cache2k

Cache2k 是一種記憶體內快取。 如果 Cache2k Spring Integration 存在,則會自動組態 `SpringCache2kCacheManager`。

可以透過設定 `spring.cache.cache-names` 屬性在啟動時建立快取。 可以使用 `Cache2kBuilderCustomizer` bean 自訂快取預設值。 下列範例顯示了一個自訂器,該自訂器將快取的容量組態為 200 個項目,到期時間為 5 分鐘

  • Java

  • Kotlin

import java.util.concurrent.TimeUnit;

import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyCache2kDefaultsConfiguration {

	@Bean
	public Cache2kBuilderCustomizer myCache2kDefaultsCustomizer() {
		return (builder) -> builder.entryCapacity(200)
				.expireAfterWrite(5, TimeUnit.MINUTES);
	}

}
import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.TimeUnit

@Configuration(proxyBeanMethods = false)
class MyCache2kDefaultsConfiguration {

	@Bean
	fun myCache2kDefaultsCustomizer(): Cache2kBuilderCustomizer {
		return Cache2kBuilderCustomizer { builder ->
			builder.entryCapacity(200)
				.expireAfterWrite(5, TimeUnit.MINUTES)
		}
	}
}

簡單

如果找不到任何其他提供者,則會組態使用 `ConcurrentHashMap` 作為快取儲存的簡單實作。 如果您的應用程式中沒有快取庫,則這是預設值。 預設情況下,快取是根據需要建立的,但您可以透過設定 `cache-names` 屬性來限制可用快取的清單。 例如,如果您只想要 `cache1` 和 `cache2` 快取,請將 `cache-names` 屬性設定如下

  • 屬性

  • YAML

spring.cache.cache-names=cache1,cache2
spring:
  cache:
    cache-names: "cache1,cache2"

如果您這樣做,並且您的應用程式使用了未列出的快取,則在需要快取時,執行階段會失敗,但啟動時不會失敗。 這與如果您使用未宣告的快取時,「真實」快取提供者的行為方式類似。

當您的組態中存在 `@EnableCaching` 時,也預期會有合適的快取組態。 如果您有自訂 `CacheManager`,請考慮在單獨的 `@Configuration` 類別中定義它,以便您可以在必要時覆寫它。 `none` 使用 no-op 實作,這在測試中很有用,切片測試預設透過 `@AutoConfigureCache` 使用它。

如果您需要在特定環境中使用 no-op 快取而不是自動組態的快取管理器,請將快取類型設定為 `none`,如下列範例所示

  • 屬性

  • YAML

spring.cache.type=none
spring:
  cache:
    type: "none"