缓存
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 会自动配置一个 简单提供者,其在内存中使用并发 Map。当需要缓存(如上述示例中的 piDecimals
)时,该提供者会为你创建缓存。简单提供者不推荐用于生产环境,但非常适合入门和理解特性。当你决定使用哪种缓存提供者后,请务必阅读其文档,了解如何配置应用所用缓存。几乎所有提供者都要求你显式配置应用中用到的每个缓存。有些还支持通过 spring.cache.cache-names
属性自定义默认缓存。
支持的缓存提供者
缓存抽象本身不提供实际存储,而是依赖 Cache
和 CacheManager
接口实现。
如果你没有定义类型为 CacheManager
的 bean,或没有名为 cacheResolver
的 CacheResolver
(见 CachingConfigurer
),Spring Boot 会按如下顺序尝试检测以下提供者:
-
JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan 等)
此外,https://github.com/spring-projects/spring-boot-data-geode[Spring Boot for Apache Geode] 提供了 使用 Apache Geode 作为缓存提供者的自动配置。
如果 CacheManager 由 Spring Boot 自动配置,可以通过设置 spring.cache.type 属性 强制 使用特定缓存提供者。如果你需要在某些环境(如测试)下 使用 no-op 缓存,可用此属性。
|
使用 spring-boot-starter-cache starter 可快速添加基础缓存依赖。该 starter 会引入 spring-context-support 。如果手动添加依赖,需确保包含 spring-context-support 才能使用 JCache 或 Caffeine 支持。
|
如果 CacheManager
由 Spring Boot 自动配置,还可以在其完全初始化前通过暴露实现了 CacheManagerCustomizer
接口的 bean 进一步调整配置。如下示例设置了一个标志,表示不应将 null
值传递给底层 map:
-
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,则会创建一个包装所有该类型 bean 的 CacheManager
。
JCache (JSR-107)
JCache 通过 classpath 上存在 CachingProvider
(即存在 JSR-107 兼容缓存库)引导,spring-boot-starter-cache
starter 提供 JCacheCacheManager
。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 ,也会自动用于 CacheManager ,除非指定了 spring.cache.jcache.config 属性。
|
自定义底层 CacheManager
有两种方式:
-
通过设置
spring.cache.cache-names
属性可在启动时创建缓存。如果定义了自定义Configuration
bean,则用于定制缓存。 -
JCacheManagerCustomizer
bean 会以CacheManager
引用进行回调,实现完全自定义。
如果定义了标准 CacheManager bean,会自动包装为缓存抽象所需的 CacheManager 实现,不再应用其他自定义。
|
Hazelcast
Spring Boot 对 Hazelcast 有 通用支持。
如果自动配置了 HazelcastInstance
且 classpath 上有 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-jakarta
和 infinispan-commons-jakarta
必须分别替换 infinispan-core
和 infinispan-commons
。
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"
默认会添加 key 前缀,确保两个不同缓存使用相同 key 时,Redis 不会出现键冲突导致返回无效值。强烈建议自定义 RedisCacheManager 时保持此设置开启。
|
通过自定义 RedisCacheConfiguration @Bean ,可完全控制默认配置。这对于自定义序列化策略很有用。
|
如需更细致的配置,可注册 RedisCacheManagerBuilderCustomizer
bean。如下示例展示了为 cache1
和 cache2
配置特定存活时间的定制器:
-
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
定义缓存规格 -
定义
CaffeineSpec
bean -
定义
Caffeine
bean
例如,以下配置创建了 cache1
和 cache2
,最大容量为 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
属性限制可用缓存列表。例如,仅允许 cache1
和 cache2
:
-
Properties
-
YAML
spring.cache.cache-names=cache1,cache2
spring:
cache:
cache-names: "cache1,cache2"
如果这样设置,应用使用未声明的缓存时会在运行时失败(而非启动时),这与“真实”缓存提供者的行为一致。
无
当配置中存在 @EnableCaching
时,也应有合适的缓存配置。如果有自定义 org.springframework.cache.CacheManager
,建议在单独的 @Configuration
类中定义,以便必要时覆盖。None 使用 no-op 实现,适用于测试,切片测试默认通过 @AutoConfigureCache
使用该实现。
如需在某些环境下使用 no-op 缓存而非自动配置的缓存管理器,可将缓存类型设为 none
,如下所示:
-
Properties
-
YAML
spring.cache.type=none
spring:
cache:
type: "none"