11.1. 缓存

Spring 框架支持透明地向应用程序添加缓存。其核心是,抽象将缓存应用于方法,从而减少基于缓存中可用信息的执行次数。缓存逻辑是透明应用的,不会对调用者造成任何干扰。只要使用@EnableCaching注释启用缓存支持,Spring Boot 就会自动配置缓存基础设施。

检查Spring 框架参考的 相关部分以获取更多详细信息。

简而言之,要向服务的操作添加缓存,请向其方法添加相关注释,如以下示例所示:

@Component
public class MyMathService {

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

}

此示例演示了如何在可能成本高昂的操作中使用缓存。在调用之前computePiDecimal,抽象会在piDecimals缓存中查找与参数i匹配的条目。如果找到条目,则立即将缓存中的内容返回给调用者,并且不调用该方法。否则,将调用该方法,并在返回值之前更新缓存。

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

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

还可以透明地从缓存中 更新逐出数据。

11.1.1. 支持的缓存提供程序

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

如果您尚未定义类型CacheManagerCacheResolver命名的 cacheResolver bean(请参阅 参考资料CachingConfigurer),Spring Boot 会尝试检测以下提供程序(按指定的顺序):

  1. JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)

此外,Spring Boot for Apache Geode提供了使用 Apache Geode 作为缓存提供程序的自动配置

如果Spring Boot 自动配置CacheManager,则可以通过设置spring.cache.type属性来强制使用特定的缓存提供程序。如果您需要在某些环境(例如测试)中 使用无操作缓存,请使用此属性。

使用spring-boot-starter-cache“Starter”快速添加基本的缓存依赖项。启动器引入了spring-context-support。如果手动添加依赖项,则必须添加依赖项spring-context-support才能使用 JCache 或 Caffeine 支持。

如果Spring Boot 自动配置CacheManager,您可以在完全初始化之前通过公开实现CacheManagerCustomizer接口的 bean 来进一步调整其配置。以下示例设置一个标志来表示null值不应向下传递到底层映射:

@Configuration(proxyBeanMethods = false)
public class MyCacheManagerConfiguration {

    @Bean
    public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
        return (cacheManager) -> cacheManager.setAllowNullValues(false);
    }

}

在前面的示例中,需要 自动配置 ConcurrentMapCacheManager。如果情况并非如此(您提供了自己的配置或自动配置了不同的缓存提供程序),则根本不会调用定制器。您可以拥有任意数量的定制器,也可以使用@OrderOrdered来进行排序。

通用的

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

JCache (JSR-107)

JCache通过类路径上存在的 javax.cache.spi.CachingProvider进行引导(即,类路径上存在符合 JSR-107 的缓存库),并且由spring-boot-starter-cache “Starter”提供JCacheCacheManager。提供各种兼容的库,而且Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供依赖管理。也可以添加任何其他兼容的库。

可能会出现多个提供者,在这种情况下必须显式指定该提供者。即使 JSR-107 标准没有强制采用标准化方法来定义配置文件的位置,Spring Boot 也会尽力通过实现细节来设置缓存,如以下示例所示:

# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.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.JCacheManagerCustomizerbeans 通过CacheManager 的引用来调用以实现完全定制。

如果定义了 标准javax.cache.CacheManagerbean,它会自动包装在org.springframework.cache.CacheManager抽象期望的实现中。没有对其应用进一步的定制。

Hazelcast

Spring Boot对 Hazelcast 具有普遍支持。如果 HazelcastInstance已自动配置并且com.hazelcast:hazelcast-spring位于类路径上,则它会自动包装在CacheManager.

Hazelcast 可以用作 JCache 兼容缓存或 SpringCacheManager兼容缓存。当设置spring.cache.typehazelcast时,Spring Boot 将使用基于CacheManager的实现。如果您想使用 Hazelcast 作为 JCache 兼容缓存,请设置spring.cache.typejcache。如果您有多个符合 JCache 标准的缓存提供程序并希望强制使用 Hazelcast,则必须显式设置 JCache 提供程序

Infinispan

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

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 分钟:

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

如果您需要对配置进行更多控制,请考虑注册CouchbaseCacheManagerBuilderCustomizerbean。以下示例显示了为cache1cache2配置特定条目到期时间的定制程序:

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

    }

}

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

默认情况下,会添加一个键前缀,这样,如果两个单独的缓存使用相同的键,Redis 不会有重叠的键,也不会返回无效值。如果您创建自己的RedisCacheManager.

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

如果您需要对配置进行更多控制,请考虑注册RedisCacheManagerBuilderCustomizerbean。以下示例显示了配置特定生存时间cache1cache2的定制程序:

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

    }

}

Caffeine

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

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

  2. 定义了一个com.github.benmanes.caffeine.cache.CaffeineSpecbean

  3. 定义了一个com.github.benmanes.caffeine.cache.Caffeinebean

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

spring.cache.cache-names=cache1,cache2
spring.cache.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属性在启动时创建缓存。可以使用Cache2kBuilderCustomizerbean 自定义缓存默认值。以下示例显示了一个自定义程序,它将缓存容量配置为 200 个条目,过期时间为 5 分钟:

@Configuration(proxyBeanMethods = false)
public class MyCache2kDefaultsConfiguration {

    @Bean
    public Cache2kBuilderCustomizer myCache2kDefaultsCustomizer() {
        return (builder) -> builder.entryCapacity(200)
                .expireAfterWrite(5, TimeUnit.MINUTES);
    }

}

Simple

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

spring.cache.cache-names=cache1,cache2

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

None

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

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

spring.cache.type=none

最后更新于