跳到主要内容
版本:3.5.10

Caching

QWen Max 中英对照 Caching

Spring Framework 提供了对透明地向应用程序添加缓存的支持。其核心抽象将缓存应用于方法,从而根据缓存中可用的信息减少方法的执行次数。缓存逻辑是透明应用的,不会对调用方造成任何干扰。更多详细信息,请参阅 Spring Framework 参考文档的相关章节

只要使用 @EnableCaching 注解启用了缓存支持,Spring Boot 就会自动配置缓存基础设施。

提示

避免在主方法的应用类上添加 @EnableCaching。这样做会使缓存成为一项强制性功能,包括 在运行测试套件时

要为服务的某个操作添加缓存,请在其方法上添加相应的注解,如下例所示:

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

@Component
public class MyMathService {

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

}

此示例演示了在可能代价较高的操作上使用缓存。在调用 computePiDecimal 之前,该抽象会查找 piDecimals 缓存中与 precision 参数匹配的条目。如果找到条目,则立即返回缓存中的内容给调用者,并且不会调用该方法。否则,将调用该方法,并在返回值之前更新缓存。

警告

你也可以透明地使用标准的 JSR-107(JCache)注解(例如 @CacheResult)。然而,我们强烈建议不要混用 Spring Cache 和 JCache 注解。

如果你没有添加任何特定的缓存库,Spring Boot 会自动配置一个 简单提供者,该提供者使用内存中的并发映射(concurrent maps)。当需要缓存时(例如前面示例中的 piDecimals),该提供者会为你自动创建缓存。虽然这个简单提供者并不真正推荐用于生产环境,但它非常适合入门阶段,帮助你理解相关功能。当你确定要使用的缓存提供者后,请务必阅读其文档,以了解如何配置应用程序所使用的缓存。几乎所有提供者都要求你显式配置应用程序中使用的每个缓存。有些提供者还支持通过 spring.cache.cache-names 属性自定义默认缓存。

提示

还可以透明地更新逐出缓存中的数据。

支持的缓存提供者

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

如果你没有定义类型为 CacheManager 的 Bean,或者没有定义名为 cacheResolverCacheResolver(参见 CachingConfigurer),Spring Boot 会尝试按以下顺序检测下列缓存提供者:

  1. Generic

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

  3. Hazelcast

  4. Infinispan

  5. Couchbase

  6. Redis

  7. Caffeine

  8. Cache2k

  9. Simple

提示

如果 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 值不应传递到底层的 map 中:

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);
}

}
备注

在前面的示例中,预期会自动配置一个 ConcurrentMapCacheManager。如果情况并非如此(即你提供了自己的配置,或者自动配置了其他缓存提供者),则根本不会调用该定制器。你可以拥有任意数量的定制器,并且还可以通过使用 @OrderOrdered 对它们进行排序。

Generic

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

JCache (JSR-107)

JCache 通过在 classpath 中存在一个 CachingProvider(即 classpath 中存在一个符合 JSR-107 规范的缓存库)来引导启动,而 spring-boot-starter-cache starter 提供了 JCacheCacheManager。有多种符合规范的库可供使用,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖管理。也可以添加任何其他符合规范的库。

可能会存在多个提供者的情况,此时必须显式指定所使用的提供者。尽管 JSR-107 标准并未强制规定配置文件位置的标准化方式,但 Spring Boot 仍会尽力支持通过实现细节来设置缓存,如下例所示:

spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.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 位于 classpath 中,它会自动被包装为一个 CacheManager

备注

Hazelcast 可以用作符合 JCache 规范的缓存,也可以用作符合 Spring CacheManager 规范的缓存。当将 spring.cache.type 设置为 hazelcast 时,Spring Boot 将使用基于 CacheManager 的实现。如果你想将 Hazelcast 用作符合 JCache 规范的缓存,请将 spring.cache.type 设置为 jcache。如果你有多个符合 JCache 规范的缓存提供者,并希望强制使用 Hazelcast,则必须显式设置 JCache 提供者

Infinispan

Infinispan 没有默认的配置文件位置,因此必须显式指定。否则,将使用默认的引导配置。

spring.cache.infinispan.config=infinispan.xml

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

有关更多详情,请参阅 文档

Couchbase

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

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

如果你需要对配置进行更精细的控制,可以考虑注册一个 CouchbaseCacheManagerBuilderCustomizer Bean。以下示例展示了一个自定义器,它为 cache1cache2 配置了特定的条目过期时间:

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)));

}

}

Redis

如果 Redis 可用并已配置,则会自动配置一个 RedisCacheManager。可以通过设置 spring.cache.cache-names 属性在启动时创建额外的缓存,并且可以使用 spring.cache.redis.* 属性来配置缓存的默认值。例如,以下配置会创建名为 cache1cache2 的缓存,其 time to live(生存时间)为 10 分钟:

spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=10m
备注

默认情况下,会添加一个键前缀,以确保当两个独立的缓存使用相同的键时,Redis 不会出现键重叠,从而不会返回无效的值。如果你创建自己的 RedisCacheManager,我们强烈建议保持此设置启用。

提示

你可以通过添加一个自己的 RedisCacheConfiguration @Bean 来完全控制默认配置。如果你需要自定义默认的序列化策略,这会非常有用。

如果你需要对配置进行更精细的控制,可以考虑注册一个 RedisCacheManagerBuilderCustomizer Bean。以下示例展示了一个自定义器,它为 cache1cache2 配置了特定的生存时间(TTL):

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)));

}

}

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,time to live 为 10 分钟。

spring.cache.cache-names=cache1,cache2
spring.cache.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 分钟:

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);
}

}

Simple

如果找不到其他提供者,则会配置一个使用 ConcurrentHashMap 作为缓存存储的简单实现。如果你的应用程序中没有包含任何缓存库,这就是默认行为。默认情况下,缓存会按需创建,但你可以通过设置 cache-names 属性来限制可用缓存的列表。例如,如果你只希望使用 cache1cache2 缓存,可以按如下方式设置 cache-names 属性:

spring.cache.cache-names=cache1,cache2

如果你这样做,并且你的应用程序使用了未列出的缓存,那么当需要该缓存时,应用程序会在运行时失败,而不是在启动时失败。这与“真实”的缓存提供者在你使用未声明的缓存时的行为类似。

None

如果你需要在某个环境中使用 no-op 缓存而不是自动配置的缓存管理器,请将缓存类型设置为 none,如下例所示:

spring.cache.type=none

测试

在运行测试套件时,通常使用一个 no-op 实现会很有用。本节列出了一些对测试有用的策略。

当定义了自定义的 CacheManager 时,最佳做法是确保缓存配置定义在一个独立的 @Configuration 类中。这样做可以确保切片测试(slice tests)不需要依赖缓存功能。对于启用完整上下文的测试(例如 @SpringBootTest),则需要显式地提供一个覆盖常规配置的配置。

如果缓存是自动配置的,则会有更多选项可用。切片测试使用 @AutoConfigureCache 注解,该注解会将自动配置的 CacheManager 替换为一个无操作(no-op)的实现。集成测试也可以通过如下方式注解相关的测试类来利用此特性:

import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@AutoConfigureCache
public class MyIntegrationTests {

// Tests use a no-op cache manager

}

另一种选择是强制为自动配置的 CacheManager 使用一个空操作(no-op)实现:

spring.cache.type=none