缓存

Spring 框架提供支持,可以透明地将缓存添加到应用程序中。从本质上讲,该抽象将缓存应用于方法,从而根据缓存中可用的信息减少执行次数。缓存逻辑是透明地应用的,不会对调用者造成任何干扰。Spring Boot 自动配置缓存基础设施,只要使用 @EnableCaching 注释启用缓存支持即可。

有关更多详细信息,请查看 Spring 框架参考中的相关部分

简而言之,要将缓存添加到服务操作中,请在其方法中添加相关注释,如下例所示

  • 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 缓存中与 i 参数匹配的条目。如果找到条目,则立即将缓存中的内容返回给调用方,并且不会调用该方法。否则,将调用该方法,并在返回该值之前更新缓存。

您也可以透明地使用标准 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 属性来 *强制* 使用特定的缓存提供者。如果您需要在某些环境(例如测试)中 使用无操作缓存,请使用此属性。
使用 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。如果不是这种情况(您提供了自己的配置或自动配置了不同的缓存提供者),则不会调用自定义程序。您可以拥有任意数量的自定义程序,也可以使用 @OrderOrdered 对它们进行排序。

通用

如果上下文定义了 *至少* 一个 org.springframework.cache.Cache Bean,则使用通用缓存。将创建包装所有此类 Bean 的 CacheManager

JCache (JSR-107)

JCache 通过类路径上存在 javax.cache.spi.CachingProvider(即类路径上存在 JSR-107 兼容的缓存库)来引导,并且 JCacheCacheManagerspring-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-jakartainfinispan-commons-jakarta 来代替 infinispan-coreinfinispan-commons

Couchbase

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

  • 属性

  • 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 分钟。

  • 属性

  • 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“启动器”提供)。可以通过以下方法之一(按指示的顺序)在启动时创建缓存,并对其进行自定义。

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

  2. 定义了一个com.github.benmanes.caffeine.cache.CaffeineSpec bean

  3. 定义了一个com.github.benmanes.caffeine.cache.Caffeine bean

例如,以下配置创建了cache1cache2缓存,它们的最大大小为 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 集成,则会自动配置一个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 使用一个无操作实现,这在测试中很有用,并且切片测试默认情况下通过 `@AutoConfigureCache` 使用它。

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

  • 属性

  • YAML

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