缓存

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 属性定义的默认缓存。

还可以透明地 更新清除 缓存中的数据。

支持的缓存提供程序

缓存抽象不提供实际的存储,而是依赖于由 CacheCacheManager 接口实现的抽象。

如果您没有定义类型为 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. 简单

此外,https://github.com/spring-projects/spring-boot-data-geode[Spring Boot for Apache Geode] 提供了 使用 Apache Geode 作为缓存提供程序的自动配置

如果 CacheManager 由 Spring Boot 自动配置,可以通过设置 spring.cache.type 属性来强制使用特定的缓存提供程序。 如果您需要在某些环境(如测试)中 使用无操作缓存,请使用此属性。
使用 spring-boot-starter-cache starter 可以快速添加基本缓存依赖项。 该 starter 引入了 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。 如果不是这种情况(您提供了自己的配置或自动配置了不同的缓存提供程序),则不会调用自定义器。 您可以根据需要拥有任意数量的自定义器,还可以使用 @OrderOrdered 对它们进行排序。

通用

如果上下文定义了至少一个 Cache bean,则使用通用缓存。 创建一个包装该类型的所有 bean 的 CacheManager

JCache (JSR-107)

JCache 通过类路径上存在 CachingProvider(即类路径上存在符合 JSR-107 的缓存库)来引导,并且 JCacheCacheManagerspring-boot-starter-cache starter 提供。 有各种符合规范的库可用,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖管理。 也可以添加任何其他符合规范的库。

可能会出现多个提供程序的情况,在这种情况下,必须明确指定提供程序。 即使 JSR-107 标准没有强制规定定义配置文件位置的标准方式,Spring Boot 也会尽最大努力适应使用实现细节设置缓存,如下例所示:

  • Properties

  • YAML

spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml
# 仅在有多个提供程序时必要
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
当缓存库同时提供本机实现和 JSR-107 支持时,Spring Boot 优先使用 JSR-107 支持,这样如果您切换到不同的 JSR-107 实现,相同的功能仍然可用。
Spring Boot 对 Hazelcast 有 通用支持。 如果有一个 HazelcastInstance 可用,除非指定了 spring.cache.jcache.config 属性,否则它也会自动重用于 CacheManager

有两种方法可以自定义底层 CacheManager

  • 可以通过设置 spring.cache.cache-names 属性在启动时创建缓存。 如果定义了自定义的 Configuration bean,则使用它来自定义它们。

  • JCacheManagerCustomizer bean 使用 CacheManager 的引用来调用,以进行完全自定义。

如果定义了标准的 CacheManager bean,它会自动包装在抽象期望的 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 没有默认的配置文件位置,因此必须明确指定。 否则,使用默认引导。

  • Properties

  • 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-jakartainfinispan-commons-jakarta 代替 infinispan-coreinfinispan-commons

Couchbase

如果 Spring Data Couchbase 可用并且已 配置 Couchbase,则会自动配置 CouchbaseCacheManager。 可以通过设置 spring.cache.cache-names 属性在启动时创建其他缓存,并且可以使用 spring.cache.couchbase.* 属性配置缓存默认值。 例如,以下配置创建了 cache1cache2 缓存,条目过期时间为 10 分钟:

  • Properties

  • YAML

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

如果您需要对配置进行更多控制,请考虑注册一个 CouchbaseCacheManagerBuilderCustomizer bean。 以下示例显示了一个自定义器,它为 cache1cache2 配置了特定的条目过期时间:

  • 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.* 属性配置缓存默认值。 例如,以下配置创建了 cache1cache2 缓存,生存时间为 10 分钟:

  • Properties

  • 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。 以下示例显示了一个自定义器,它为 cache1cache2 配置了特定的生存时间:

  • 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 starter 提供)。 可以通过设置 spring.cache.cache-names 属性在启动时创建缓存,并可以通过以下方式之一(按指示顺序)进行自定义:

  1. spring.cache.caffeine.spec 定义的缓存规范

  2. 定义了 CaffeineSpec bean

  3. 定义了 Caffeine bean

例如,以下配置创建了 cache1cache2 缓存,最大大小为 500,生存时间为 10 分钟:

  • Properties

  • 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"

如果定义了 CacheLoader bean,它会自动关联到 CaffeineCacheManager。 由于 CacheLoader 将关联到缓存管理器管理的所有缓存,因此必须将其定义为 CacheLoader<Object, Object>。 自动配置会忽略任何其他泛型类型。

Cache2k

Cache2k 是一个内存缓存。 如果存在 Cache2k spring 集成,则会自动配置 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 属性来限制可用缓存的列表。 例如,如果您只想要 cache1cache2 缓存,请按如下方式设置 cache-names 属性:

  • Properties

  • YAML

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

如果您这样做,并且您的应用程序使用了未列出的缓存,则当需要缓存但未在启动时声明时,它会在运行时失败。 这与"真实"缓存提供程序在您使用未声明的缓存时的行为类似。

当配置中存在 @EnableCaching 时,也需要合适的缓存配置。 如果您有自定义的 org.springframework.cache.CacheManager,请考虑在单独的 @Configuration 类中定义它,以便在必要时可以覆盖它。 None 使用无操作实现,这在测试中很有用,切片测试通过 @AutoConfigureCache 默认使用它。

如果您需要在特定环境中使用无操作缓存而不是自动配置的缓存管理器,请将缓存类型设置为 none,如下例所示:

  • Properties

  • YAML

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