缓存

Spring框架为向应用程序透明地添加缓存提供了支持。 其核心是将缓存应用于方法,从而根据缓存中的信息减少执行次数。 缓存逻辑是透明应用的,不会对调用者造成任何干扰。 有关详细信息,请查看Spring框架参考文档的相关部分spring-doc.cadn.net.cn

只要使用 @EnableCaching 注解启用缓存支持,Spring Boot 就会自动配置缓存基础设施。spring-doc.cadn.net.cn

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

要为服务的某个操作添加缓存,请向其方法添加相应的注解,如下例所示:spring-doc.cadn.net.cn

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

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

如果不添加任何特定的缓存库,Spring Boot 会自动配置一个 简单提供程序,该提供程序在内存中使用并发映射。 当需要缓存时(例如前面示例中的 piDecimals),此提供程序会为您创建它。 简单提供程序不适用于生产环境,但非常适合入门并确保您了解其功能。 当您决定使用哪个缓存提供程序后,请务必阅读其文档,以了解如何配置应用程序使用的缓存。 几乎所有的提供程序都要求您显式配置应用程序中使用的每个缓存。 某些提供程序还提供了一种自定义由 spring.cache.cache-names 属性定义的默认缓存的方法。spring-doc.cadn.net.cn

也可以透明地 更新清除 缓存中的数据。

支持的缓存提供者

缓存抽象不提供实际的存储,而是依赖于由 CacheCacheManager 接口具体化的抽象。spring-doc.cadn.net.cn

如果您未定义类型为 CacheManager 或名为 cacheResolverCacheResolver(参见 CachingConfigurer),Spring Boot 会尝试按以下顺序检测提供者:spring-doc.cadn.net.cn

如果 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 值不应传递到底层映射中:spring-doc.cadn.net.cn

import org.springframework.boot.cache.autoconfigure.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.cache.autoconfigure.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 对它们进行排序。

泛型

如果上下文定义了至少一个 Cache bean,则使用通用缓存。 系统会创建一个 CacheManager 来包装该类型的所有 bean。spring-doc.cadn.net.cn

JCache (JSR-107)

JCache 通过类路径上存在 CachingProvider(即类路径上存在符合 JSR-107 规范的缓存库)来引导启动,而 JCacheCacheManagerspring-boot-starter-cache starter 提供。 有多种符合规范的库可用,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖管理。 也可以添加其他任何符合规范的库。spring-doc.cadn.net.cn

可能会出现有多个提供者的情况,在这种情况下,必须显式指定提供者。 即使 JSR-107 标准没有强制规定定义配置文件位置的标准化方式,Spring Boot 会尽力适应设置带有实现细节的缓存,如下例所示:spring-doc.cadn.net.cn

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 属性。

有两种方法可以自定义底层的 CacheManagerspring-doc.cadn.net.cn

如果定义了标准的 CacheManager bean,它会被自动包装在抽象层所期望的 CacheManager 实现中。 不会对其应用进一步的自定义配置。

Hazelcast

Spring Boot 对 Hazelcast 有通用支持。 如果已自动配置了 HazelcastInstance,并且 com.hazelcast:hazelcast-spring 在类路径上,它将被自动包装在 CacheManager 中。spring-doc.cadn.net.cn

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

Infinispan

Infinispan 没有默认的配置文件位置,因此必须显式指定。 否则,将使用默认引导程序。spring-doc.cadn.net.cn

spring.cache.infinispan.config=infinispan.xml
spring:
  cache:
    infinispan:
      config: "infinispan.xml"

可以通过设置 spring.cache.cache-names 属性在启动时创建缓存。 如果定义了自定义的 ConfigurationBuilder bean,它将用于自定义缓存。spring-doc.cadn.net.cn

有关更多详细信息,请参阅 文档spring-doc.cadn.net.cn

Couchbase

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

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

如果您需要对配置进行更多控制,请考虑注册一个 CouchbaseCacheManagerBuilderCustomizer bean。 以下示例展示了一个为 cache1cache2 配置特定条目过期时间的定制器:spring-doc.cadn.net.cn

import java.time.Duration;

import org.springframework.boot.cache.autoconfigure.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.cache.autoconfigure.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 分钟:spring-doc.cadn.net.cn

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 配置特定生存时间的定制器:spring-doc.cadn.net.cn

import java.time.Duration;

import org.springframework.boot.cache.autoconfigure.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.cache.autoconfigure.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-doc.cadn.net.cn

  1. spring.cache.caffeine.spec 定义的缓存规范spring-doc.cadn.net.cn

  2. 定义了一个 CaffeineSpec beanspring-doc.cadn.net.cn

  3. 定义了一个 Caffeine beanspring-doc.cadn.net.cn

例如,以下配置创建了最大大小为 500、生存时间为 10 分钟的 cache1cache2 缓存spring-doc.cadn.net.cn

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>。 自动配置会忽略任何其他泛型类型。spring-doc.cadn.net.cn

Cache2k

Cache2k 是一个内存缓存。 如果存在 Cache2k Spring 集成,则会自动配置一个 SpringCache2kCacheManagerspring-doc.cadn.net.cn

可以通过设置 spring.cache.cache-names 属性在启动时创建缓存。 可以使用 Cache2kBuilderCustomizer bean 自定义缓存默认值。 以下示例展示了一个将缓存容量配置为 200 个条目、过期时间为 5 分钟的定制器:spring-doc.cadn.net.cn

import java.util.concurrent.TimeUnit;

import org.springframework.boot.cache.autoconfigure.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.cache.autoconfigure.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 属性来限制可用缓存的列表。 例如,如果您只希望使用 cache1cache2 缓存,请按如下方式设置 cache-names 属性:spring-doc.cadn.net.cn

spring.cache.cache-names=cache1,cache2
spring:
  cache:
    cache-names: "cache1,cache2"

如果你这样做,而你的应用程序使用了一个未列出的缓存,那么在需要缓存时会在运行时失败,但不会在启动时失败。 这与使用未声明的缓存时“真正的”缓存提供者的行为类似。spring-doc.cadn.net.cn

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

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

测试

在运行测试套件时,使用一个无操作(no-op)实现通常是很有用的。 本节列出了一些对测试有用的策略。spring-doc.cadn.net.cn

当定义了自定义 CacheManager 时,最佳做法是确保在独立的 @Configuration 类中定义缓存配置。 这样做可以确保切片测试不需要缓存。 对于启用完整上下文的测试(例如 @SpringBootTest),需要显式配置来覆盖常规配置。spring-doc.cadn.net.cn

如果启用了自动缓存配置,将有更多选项可用。 测试可以用 @AutoConfigureCache 注解,以使用无操作实现替换自动配置的 CacheManagerspring-doc.cadn.net.cn

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

@SpringBootTest
@AutoConfigureCache
public class MyIntegrationTests {

	// Tests use a no-op cache manager

}
import org.springframework.boot.cache.test.autoconfigure.AutoConfigureCache
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest
@AutoConfigureCache
class MyIntegrationTests {

	// Tests use a no-op cache manager

}

另一种选择是为自动配置的 CacheManager 强制使用无操作(no-op)实现:spring-doc.cadn.net.cn

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