|
对于最新稳定版本,请使用 Spring Boot 4.0.4! |
缓存
Spring 框架提供了对透明地向应用程序添加缓存的支持。 其核心思想是将缓存应用于方法,从而根据缓存中可用的信息减少方法的执行次数。 缓存逻辑以透明的方式应用,不会对调用方造成任何干扰。 更多详细信息,请参阅 Spring 框架参考文档的相关章节。
只要通过使用 @EnableCaching 注解启用了缓存支持,Spring Boot 就会自动配置缓存基础设施。
避免将 @EnableCaching 添加到主方法的应用类中。
这样做会使缓存成为一项强制功能,包括 在运行测试套件时。 |
要为服务中的某个操作添加缓存,请在其方法上添加相应的注解,如下例所示:
-
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 会自动配置一个简单的提供者,该提供者使用内存中的并发映射(concurrent maps)。
当需要缓存时(例如前面示例中的 piDecimals),此提供者会为你自动创建缓存。
该简单提供者并不真正推荐用于生产环境,但它非常适合入门阶段,帮助你理解相关功能。
当你确定要使用的缓存提供者后,请务必阅读其文档,以了解如何配置应用程序所使用的缓存。
几乎所有缓存提供者都要求你显式配置应用程序中使用的每个缓存。
有些提供者还提供了自定义由 spring.cache.cache-names 属性定义的默认缓存的方法。
支持的缓存提供者
缓存抽象并不提供实际的存储,而是依赖于由 Cache 和 CacheManager 接口具体化的抽象。
如果您未定义类型为 CacheManager 或名为 cacheResolver 的 CacheResolver(请参阅 CachingConfigurer),Spring Boot 将尝试按以下顺序检测以下提供程序:
-
JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan 等)
如果 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。
如果情况并非如此(要么您提供了自己的配置,要么自动配置了不同的缓存提供者),则根本不会调用自定义器。
您可以拥有任意数量的自定义器,并且还可以使用 @Order 或 Ordered 对它们进行排序。 |
通用
如果上下文定义了至少一个 Cache Bean,则使用通用缓存。
系统将创建一个 CacheManager 来包装该类型的所有 Bean。
JCache (JSR-107)
JCache 通过类路径上存在 CachingProvider(即类路径上存在符合 JSR-107 规范的缓存库)进行引导,而 JCacheCacheManager 则由 spring-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
# 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 属性。 |
有两种方法可以自定义底层的 CacheManager:
-
缓存可以在启动时通过设置
spring.cache.cache-names属性来创建。 如果定义了自定义的ConfigurationBean,则将使用它来自定义缓存。 -
JCacheManagerCustomizerBean 在调用时会引用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,则将使用它来自定义缓存。
有关更多详情,请参阅文档。
Couchbase
如果 Spring Data Couchbase 可用且 Couchbase 已配置,则会自动配置一个 CouchbaseCacheManager。
可以通过设置 spring.cache.cache-names 属性在启动时创建额外的缓存,并且可以使用 spring.cache.couchbase.* 属性来配置缓存默认值。
例如,以下配置创建了 cache1 和 cache2 缓存,其条目过期时间为 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。
以下示例展示了一个自定义器,用于为 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 分钟:
-
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。
以下示例展示了一个自定义器,用于为 cache1 和 cache2 配置特定的存活时间(TTL):
-
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 属性在启动时创建缓存,并可通过以下选项之一进行自定义(按指示顺序):
-
由
spring.cache.caffeine.spec定义的缓存规格 -
一个
CaffeineSpecBean 被定义 -
一个
CaffeineBean 被定义
例如,以下配置会创建 cache1 和 cache2 缓存,其最大容量为 500,生存时间(time to live)为 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 属性来限制可用缓存的列表。
例如,如果您只想要 cache1 和 cache2 缓存,请按如下方式设置 cache-names 属性:
-
Properties
-
YAML
spring.cache.cache-names=cache1,cache2
spring:
cache:
cache-names: "cache1,cache2"
如果你这样做,而你的应用程序使用了未列出的缓存,那么在运行时当需要该缓存时会失败,但不会在启动时失败。 这与你使用未声明的缓存时,“真实”的缓存提供者的行为类似。
测试
在运行测试套件时,通常使用一个空操作(no-op)实现会很有帮助。 本节列出了一些对测试有用的策略。
当定义了自定义的 CacheManager 时,最佳做法是确保缓存配置定义在一个隔离的 @Configuration 类中。
这样做可以确保切片测试不需要缓存。
对于启用完整上下文的测试(例如 @SpringBootTest),则需要显式配置来覆盖常规配置。
如果缓存是自动配置的,则可用选项更多。
切片测试使用 @AutoConfigureCache 进行注解,它会将自动配置的 CacheManager 替换为一个无操作实现。
集成测试也可以通过如下方式注解相关测试类来受益于该功能:
-
Java
-
Kotlin
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
}
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
@AutoConfigureCache
class MyIntegrationTests {
// Tests use a no-op cache manager
}
另一个选项是强制为自动配置的 CacheManager 提供一个空操作实现:
-
Properties
-
YAML
spring.cache.type=none
spring:
cache:
type: "none"