缓存

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 会自动配置一个使用并发映射(concurrent maps)存储在内存中的简单提供程序。当需要缓存时(例如前面示例中的piDecimals),此提供程序会为您创建它。简单提供程序不推荐用于生产环境,但非常适合入门并确保您理解其功能。当您决定要使用的缓存提供程序时,请务必阅读其文档以了解如何配置应用程序使用的缓存。几乎所有提供程序都需要您显式配置应用程序中使用的每个缓存。有些提供程序提供了一种方法来自定义由spring.cache.cache-names属性定义的默认缓存。

还可以透明地更新删除缓存中的数据。

支持的缓存提供程序

缓存抽象不提供实际的存储,而是依赖于由org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口实现的抽象。

如果您未定义类型为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启动器快速添加基本的缓存依赖项。启动器引入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。如果不是这种情况(您提供了自己的配置或自动配置了不同的缓存提供程序),则根本不会调用自定义程序。您可以拥有任意数量的自定义程序,并且还可以通过使用@OrderOrdered对其进行排序。

通用

如果上下文定义了至少一个org.springframework.cache.Cache Bean,则使用通用缓存。将创建一个包装所有此类 Bean 的CacheManager

JCache (JSR-107)

JCache通过类路径上存在javax.cache.spi.CachingProvider(即类路径上存在符合 JSR-107 的缓存库)来引导,并且JCacheCacheManagerspring-boot-starter-cache启动器提供。有各种兼容的库可用,并且 Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供了依赖项管理。还可以添加任何其他兼容库。

可能会出现多个提供程序存在的情况,在这种情况下,必须显式指定提供程序。即使 JSR-107 标准没有强制执行定义配置文件位置的标准化方法,Spring Boot 也会尽最大努力适应使用实现细节设置缓存,如下例所示

  • 属性

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

有两种方法可以自定义底层的javax.cache.cacheManager

  • 可以通过设置spring.cache.cache-names属性在启动时创建缓存。如果定义了自定义的javax.cache.configuration.Configuration Bean,则将其用于自定义它们。

  • org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer Bean 会使用CacheManager的引用来调用,以进行完全自定义。

如果定义了标准的javax.cache.CacheManager Bean,则会将其自动包装在抽象期望的org.springframework.cache.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没有默认的配置文件位置,因此必须显式指定它。否则,将使用默认引导程序。

  • 属性

  • 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-jakartainfinispan-commons-jakarta来代替infinispan-coreinfinispan-commons

Couchbase

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

  • 属性

  • YAML

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

如果您需要更多地控制配置,请考虑注册CouchbaseCacheManagerBuilderCustomizer Bean。以下示例显示了一个自定义程序,该自定义程序为cache1cache2配置了特定的条目过期时间

  • 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.*属性来配置缓存默认值。例如,以下配置创建了cache1cache2缓存,其生存时间为 10 分钟

  • 属性

  • 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。以下示例显示了一个自定义程序,该自定义程序为cache1cache2配置了特定的生存时间

  • 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启动器提供)。可以通过以下方法之一(按指示顺序)在启动时创建缓存并进行自定义

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

  2. 定义了com.github.benmanes.caffeine.cache.CaffeineSpec Bean

  3. 定义了com.github.benmanes.caffeine.cache.Caffeine Bean

例如,以下配置创建了cache1cache2缓存,其最大大小为 500,生存时间为 10 分钟

  • 属性

  • 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"

如果定义了com.github.benmanes.caffeine.cache.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属性来限制可用缓存的列表。例如,如果您只需要cache1cache2缓存,请按如下所示设置cache-names属性

  • 属性

  • YAML

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

如果您这样做并且您的应用程序使用了未列出的缓存,则在需要缓存时但在启动时不会在运行时失败。这类似于在使用未声明的缓存时“真实”缓存提供程序的行为方式。

当您的配置中存在@EnableCaching时,也需要合适的缓存配置。如果您有自定义的CacheManager,请考虑在单独的@Configuration类中定义它,以便在必要时可以覆盖它。无使用在测试中很有用的无操作实现,并且切片测试通过@AutoConfigureCache默认使用它。

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

  • 属性

  • YAML

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