跳到主要内容

缓存

DeepSeek V3 中英对照 Caching

Spring 框架提供了对应用程序透明添加缓存的支持。其核心在于将缓存应用于方法,从而根据缓存中可用的信息减少执行次数。缓存逻辑被透明地应用,不会对调用者产生任何干扰。只要通过使用 @EnableCaching 注解启用了缓存支持,Spring Boot 就会自动配置缓存基础设施。

备注

查看 Spring 框架参考文档中的相关章节以获取更多详情。

简而言之,要为服务的操作添加缓存,只需在其方法上添加相关注解,如下例所示:

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

@Component
public class MyMathService {

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

}
java

这个示例展示了在潜在高成本操作中使用缓存的用法。在调用 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. 简单

提示

如果 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 值传递到底层映射中:

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

}
java
备注

在前面的示例中,预期会有一个自动配置的 ConcurrentMapCacheManager。如果情况并非如此(要么您提供了自己的配置,要么自动配置了不同的缓存提供程序),则根本不会调用自定义器。您可以根据需要添加任意数量的自定义器,并且还可以使用 @OrderOrdered 对它们进行排序。

通用缓存提供程序

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

JCache (JSR-107)

JCache 的启动是通过在类路径下存在一个 CachingProvider 来实现的(即类路径下存在一个符合 JSR-107 规范的缓存库),而 JCacheCacheManager 则是由 spring-boot-starter-cache 启动器提供的。目前有各种符合规范的库可供选择,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖管理。当然,也可以添加任何其他符合规范的库。

可能会出现多个 provider 的情况,此时必须显式指定 provider。即使 JSR-107 标准没有强制规定定义配置文件位置的标准方式,Spring Boot 仍然会尽力适应实现细节来设置缓存,如下例所示:

spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml
properties
备注

当缓存库同时提供了原生实现和 JSR-107 支持时,Spring Boot 会优先选择 JSR-107 支持,这样即使你切换到不同的 JSR-107 实现,也能使用相同的功能。

提示

Spring Boot 对 Hazelcast 提供了通用支持。如果系统中只有一个 HazelcastInstance 可用,它也会自动被 CacheManager 复用,除非指定了 spring.cache.jcache.config 属性。

有两种方法可以自定义底层的 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 没有默认的配置文件位置,因此必须明确指定。否则,将使用默认的引导配置。

spring.cache.infinispan.config=infinispan.xml
properties

可以在启动时通过设置 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 分钟:

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

如果你需要对配置进行更多控制,可以考虑注册一个 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)));

}

}
java

Redis

如果 Redis 可用且已配置,系统将自动配置一个 RedisCacheManager。可以通过设置 spring.cache.cache-names 属性在启动时创建额外的缓存,并且可以使用 spring.cache.redis.* 属性来配置缓存的默认值。例如,以下配置将创建 cache1cache2 缓存,并将它们的 生存时间 设置为 10 分钟:

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

默认情况下,会添加一个键前缀,这样如果两个独立的缓存使用相同的键,Redis 就不会有重叠的键,也就无法返回无效的值。如果你创建自己的 RedisCacheManager,我们强烈建议保持此设置启用。

提示

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

如果你需要更精细地控制配置,可以考虑注册一个 RedisCacheManagerBuilderCustomizer Bean。以下示例展示了一个自定义配置,为 cache1cache2 配置了特定的存活时间:

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

}

}
java

咖啡因

Caffeine 是 Guava 缓存的 Java 8 重写版本,取代了对 Guava 的支持。如果存在 Caffeine,则会自动配置 CaffeineCacheManager(由 spring-boot-starter-cache 启动器提供)。可以通过设置 spring.cache.cache-names 属性在启动时创建缓存,并可以通过以下方式之一(按指定顺序)进行自定义:

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

  2. 定义了一个 CaffeineSpec Bean

  3. 定义了一个 Caffeine Bean

例如,以下配置创建了 cache1cache2 两个缓存,其最大容量为 500,且存活时间为 10 分钟。

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

如果定义了一个 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);
}

}
java

简单

如果找不到其他提供者,将配置一个使用 ConcurrentHashMap 作为缓存存储的简单实现。如果您的应用程序中没有缓存库,这将作为默认配置。默认情况下,缓存会根据需要创建,但您可以通过设置 cache-names 属性来限制可用缓存的列表。例如,如果您只想要 cache1cache2 缓存,可以如下设置 cache-names 属性:

spring.cache.cache-names=cache1,cache2
properties

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

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

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

spring.cache.type=none
properties