spring对于缓存的支持

8. 缓存抽象

从3.1版开始,Spring Framework提供了对现有Spring应用程序透明地添加缓存的支持。与事务 支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的影响最小。

从Spring 4.1开始,通过JSR-107注释和更多自定义选项的支持,缓存抽象得到了显着改进。

8.1 了解缓存抽象

缓存与缓冲区
术语“buffer”和“cache”往往可以互换使用。然而,请注意,它们代表不同的事物。传统上,缓冲区用作快速实体和慢速实体之间数据的中间临时存储区。由于一方必须等待另一方(这会影响性能),缓冲区通过允许整个数据块同时移动而不是以小块移动来减轻这一点。数据只从缓冲区写入和读取一次。此外,至少有一方知道缓冲区是可见的。

另一方面,根据定义,缓存是隐藏的,任何一方都不知道发生了缓存。它还提高了性能,但这样做是通过让同一数据以快速的方式被多次读取。

在其核心,高速缓存抽象将缓存应用到Java方法,从而减少基于缓存中可用的信息的执行次数。也就是说,每次调用一个目标方法时,抽象都会应用一个缓存行为来检查该方法是否已经为给定的参数执行过。如果已执行,则返回缓存结果而不必执行实际方法。如果尚未执行该方法,则执行该方法,并将结果缓存并返回给用户,以便下次调用该方法时返回缓存的结果。这样,对于给定的一组参数和重用的结果,昂贵的方法(无论是CPU或IO绑定的)只能执行一次,而不必再次实际执行该方法。缓存逻辑是透明应用的,不会对调用程序产生任何干扰。

缓存抽象提供了其他与缓存相关的操作,例如更新缓存内容或删除一个或所有条目的能力。如果缓存处理在应用程序运行过程中可能发生更改的数据,这些功能非常有用。

与Spring框架中的其他服务一样,缓存服务是一种抽象(不是缓存实现),需要使用实际存储来存储缓存数据-也就是说,抽象使您不必编写缓存逻辑,但不提供实际的数据存储。这个抽象由org.springframework.cache.cache和org.springframework.cache.cachemanager接口实现。

Spring提供了一些抽象实现:java.util.concurrent.ConcurrentMap基于JDK 的缓存, Ehcache 2.x,Gemfire缓存, Caffeine和符合JSR-107的缓存(例如Ehcache 3.x)

如果您有一个多进程环境(即部署在多个节点上的应用程序),则需要相应地配置缓存提供程序。根据您的使用案例,在多个节点上复制相同数据就足够了。但是,如果在应用程序的过程中更改数据,则可能需要启用其他传播机制。

缓存一个特定的项直接相当于典型的get-if-not-found-then-process,并最终放置通过编程缓存交互找到的代码块。没有应用锁,多个线程可能会尝试同时加载同一项。这同样适用于驱逐。如果多个线程试图同时更新或逐出数据,则可以使用过时的数据。某些缓存提供程序在该领域提供高级功能。有关详细信息,请参阅缓存提供程序的文档。

要使用缓存抽象,需要注意两个方面:

  • 缓存声明:标识需要缓存的方法及其策略。
  • 缓存配置:存储数据并从中读取数据的备份缓存。

8.2 基于声明注释的缓存

对于缓存声明,Spring的缓存抽象提供了一组Java注释:

  • @Cacheable:触发​​缓存填充。
  • @CacheEvict:触发​​缓存逐出。
  • @CachePut:更新缓存而不会干扰方法执行。
  • @Caching:重新组合要应用于方法的多个缓存操作。
  • @CacheConfig:在类级别共享一些常见的缓存相关设置。

8.2.1 @Cacheable注解

顾名思义,您可以使用@cacheable来划分可缓存的方法-,也就是说,将结果存储在缓存中的方法,以便在随后的调用(使用相同的参数)时,返回缓存中的值,而不必实际执行该方法。在最简单的形式中,注释声明需要与带注释的方法关联的缓存的名称,如下示例所示:

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

在前面的代码段中,findbook方法与名为books的缓存相关联。每次调用方法时,都会检查缓存,以查看调用是否已经执行,并且不必重复。在大多数情况下,只声明一个缓存,注释允许指定多个名称,以便使用多个缓存。在这种情况下,在执行方法-之前检查每个缓存。如果至少命中一个缓存,则返回相关的值。

即使未实际执行缓存的方法,也会更新不包含该值的所有其他缓存。

@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}
默认密钥生成

由于缓存本质上是键值存储,所以每次对缓存方法的调用都需要转换为适合缓存访问的键值。缓存抽象使用基于以下算法的简单密钥生成器:

  • 如果没有给定参数,则返回SimpleKey.EMPTY。
  • 如果只给出一个参数,则返回该实例。
  • 如果给定的参数不止一个,则返回包含所有参数的simplekey。

只要参数具有自然键并实现有效的hashcode()和equals()方法,这种方法对大多数用例都很有效。如果不是这样,你需要改变策略。

要提供不同的默认密钥生成器,需要实现org.springframework.cache.interceptor.KeyGenerator接口。

随着Spring 4.0的发布,默认密钥生成策略发生了变化。早期版本的Spring使用了密钥生成策略,对于多个关键参数,仅考虑hashCode()参数而不考虑参数equals()。这可能会导致意外的键碰撞( 有关背景,请参阅SPR-10237)。新的SimpleKeyGenerator使用复合键来处理这种情况。

如果要继续使用以前的密钥策略,可以配置已弃用的 org.springframework.cache.interceptor.DefaultKeyGenerator类或创建基于哈希的自定义KeyGenerator实现。

自定义密钥生成声明

由于缓存是通用的,因此目标方法很可能具有各种签名,而这些签名无法轻易映射到缓存结构的顶部。当目标方法有多个参数,其中只有一些参数适合缓存时(而其余参数仅由方法逻辑使用),这一点就会变得明显。请考虑以下示例:

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

对于上述多个参数的情况,只有第一个参数适合缓存,其他参数没有作用的情况;@Cacheable注释允许您指定通过其key属性生成密钥的方式。您可以使用SpEL选择感兴趣的参数(或其嵌套属性),执行操作,甚至调用任意方法,而无需编写任何代码或实现任何接口。这是默认生成器的推荐方法 ,因为随着代码库的增长,签名方法往往会有很大不同。虽然默认策略可能适用于某些方法,但它很少适用于所有方法。

以下示例是各种SpEL声明(如果您不熟悉SpEL,请自己帮个忙并阅读Spring Expression Language):

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

前面的代码片段显示了选择某个参数,其属性之一,甚至是任意(静态)方法是多么容易。

如果负责生成密钥的算法过于具体或者需要共享,则可以keyGenerator在操作上定义自定义。为此,请指定KeyGenerator要使用的bean实现的名称,如以下示例所示:

@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
默认缓存Resolution

缓存抽象使用一个简单的CacheResolver ,通过使用配置的CacheManager来检索在操作级别定义的缓存。

要提供不同的默认缓存解析器,需要实现org.springframework.cache.interceptor.CacheResolver接口。

自定义缓存分辨率

默认缓存分辨率非常适合使用单个缓存管理器且不需要复杂缓存分辨率要求的应用程序。

对于使用多个缓存管理器的应用程序,可以为每个操作设置要使用的缓存管理器,如下示例所示:

@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") 
public Book findBook(ISBN isbn) {...}

您也可以用类似于替换密钥生成的方式完全替换缓存解析器(CacheResolver)。每个缓存操作都请求解析,让实现根据运行时参数实际解析要使用的缓存。以下示例显示如何指定缓存冲突解决程序:

@Cacheable(cacheResolver="runtimeCacheResolver") 
public Book findBook(ISBN isbn) {...}

自Spring4.1以来,缓存注释的value属性不再是强制的,因为缓存解析器可以提供这些特定的信息,而不管注释的内容是什么。

与键和键生成器类似,cachemanager和cacheresolver参数是互斥的,指定这两个参数的操作将导致异常。因为自定义的cachemanager被cacheresolver实现忽略。这可能不是你所期望的。

同步缓存

在多线程环境中,可能会为同一参数同时调用某些操作(通常在启动时)。默认情况下,缓存抽象不会锁定任何内容,并且可能会多次计算相同的值,从而破坏了缓存的目的。

对于这些特定情况,您可以使用该sync属性指示底层缓存提供程序在计算值时锁定缓存条目。因此,只有一个线程忙于计算该值,而其他线程则被阻塞,直到该条目在缓存中更新为止。以下示例显示如何使用该sync属性:

@Cacheable(cacheNames="foos", sync=true) 
public Foo executeExpensiveOperation(String id) {...}

这是一项可选功能,您最喜欢的缓存库可能不支持它。CacheManager核心框架提供的所有 实现都支持它。有关更多详细信息,请参阅缓存提供程序的文档。

条件缓存

有时,方法可能不适合一直缓存(例如,它可能取决于给定的参数)。缓存注解通过支持这样的功能condition参数,这需要一个SpEL被评价为任一表达式true或false。如果true,该方法被缓存。如果不是,则其行为就好像该方法未被缓存(即,无论缓存中的值是什么,或者使用了什么参数,每次都执行该方法)。例如,仅当参数name的长度小于32时,才会缓存以下方法:

@Cacheable(cacheNames="book", condition="#name.length() < 32") 
public Book findBook(String name)

除Condition参数外,还可以使用unless 参数否决向缓存中添加值。与条件不同,除非表达式是在调用方法之后计算的。为了扩展前面的示例,我们可能只希望缓存paperback books,如下示例所示:

缓存抽象支持java.util.Optional,仅在其存在时才将其内容用作缓存值。#result始终引用业务实体而从不支持包装器,因此前面的示例可以重写如下:

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
public Optional<Book> findBook(String name)

注意result仍然是指Book而不是Optional。可能是这样null,我们应该使用安全导航操作符。

可用的缓存SpEL评估上下文

每个SpEL表达式都针对专用进行评估 context。除了内置参数之外,框架还提供专用的与缓存相关的元数据,例如参数名称。下表描述了上下文可用的项目,以便您可以将它们用于键和条件计算:

缓存SpEL可用元数据

Name Location Description Example
methodName Root object 正在调用的方法的名称 #root.methodName
method Root object 正在调用的方法 #root.method.name
target Root object 正在调用的目标对象 #root.target
targetClass Root object 被调用的目标的类 #root.targetClass
args Root object 用于调用目标的参数(作为数组) #root.args[0]
caches Root object 执行当前方法的高速缓存的集合 #root.caches[0].name
Argument name Evaluation context 任何方法参数的名称。如果名称不可用(可能是由于没有调试信息),参数名称也可以在#a<#arg> where #arg参数索引(从…开始0)下找到。 #iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias).
result Evaluation context 方法调用的结果(要缓存的值)。只适用于unless 表达式,cache put表达式(计算key),或cache evict 表达式(如果beforeInvocation是false)。对于受支持的包装器(例如 Optional),#result指的是实际的对象,而不是包装器。 #result

8.2.2 @CachePut注解

当需要更新缓存而不干扰方法执行时,可以使用@CachePut注释。也就是说,始终执行该方法,并将其结果放入缓存中(根据@CachePut选项)。它支持与@Cacheable缓存填充相同的选项,而不是方法流优化。以下示例使用@CachePut注释:

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)

通常强烈建议不要对同一方法 使用@CachePut和@Cacheable注释,因为它们具有不同的行为。虽然后者导致通过使用缓存跳过方法执行,但前者强制执行以执行缓存更新。这会导致意外的行为,并且除了特定的角落情况(例如注释具有将它们彼此排除的条件)之外,应该避免这种声明。另请注意,此类条件不应依赖于结果对象(即#result变量),因为这些条件已预先验证以确认排除。

8.2.3 @CacheEvict注解

缓存抽象不仅允许缓存存储的填充,还允许驱逐。此过程对于从缓存中删除陈旧或未使用的数据非常有用。相反 @Cacheable,@CacheEvict划分执行缓存逐出的方法(即,用作从缓存中删除数据的触发器的方法)。与其兄弟类似,@CacheEvict需要指定受操作影响的一个或多个缓存,允许指定自定义缓存和密钥解析或条件,并具有一个额外的参数(allEntries),指示是否需要缓存范围内的驱逐执行而不仅仅是一个条目驱逐(基于密钥)。以下示例逐出books缓存中的所有条目:

@CacheEvict(cacheNames="books", allEntries=true) 
public void loadBooks(InputStream batch)

当需要清除整个缓存区域时,此选项会派上用场。而不是驱逐每个条目(这将花费很长时间,因为它是低效的),所有条目在一个操作中被移除,如前面的示例所示。请注意,框架会忽略此方案中指定的任何键,因为它不适用(整个缓存被驱逐,而不仅仅是一个条目)。

您还可以指示驱逐是在(默认)之后还是在方法执行之前使用beforeInvocation属性进行。前者提供与其他注释相同的语义:一旦方法成功完成,就会执行缓存上的操作(在本例中为驱逐)。如果方法未执行(因为它可能被缓存)或抛出异常,则不会发生驱逐。后者(beforeInvocation=true)导致驱逐始终在调用方法之前发生。这在驱逐不需要与方法结果相关联的情况下非常有用。

请注意,void方法可以使用@CacheEvict- 由于方法充当触发器,因此忽略返回值(因为它们不与缓存交互)。这不是将@Cacheable数据添加或更新到高速缓存中的情况,因此需要结果。

8.2.4 @Caching注解

有时,需要指定相同类型(例如@CacheEvict或 @CachePut)的多个注释- 例如,因为不同高速缓存之间的条件或键表达式不同。@Caching允许多个嵌套 @Cacheable,@CachePut和@CacheEvict注解相同的方法来使用。以下示例使用两个@CacheEvict注释:

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)

8.2.5 @CacheConfig注解

到目前为止,我们已经看到缓存操作提供了许多自定义选项,您可以为每个操作设置这些选项。但是,如果某些自定义选项适用于该类的所有操作,则它们可能会很繁琐。例如,指定用于类的每个高速缓存操作的高速缓存的名称可以由单个类级别定义替换。这是@CacheConfig 发挥作用的地方。以下示例用于@CacheConfig设置缓存的名称:

@CacheConfig("books") 
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
}

@CacheConfig是一个类级注释,允许共享缓存名称,自定义 KeyGenerator,自定义CacheManager和自定义CacheResolver。将此批注放在类上不会打开任何缓存操作。

操作级自定义总是覆盖@cacheconfig上的自定义集。因此,这为每个缓存操作提供了三个级别的自定义:

  • Globally configured, available for CacheManager, KeyGenerator.
  • At the class level, using @CacheConfig.
  • At the operation level.

8.2.6 启用缓存注释

重要的是要注意,即使声明缓存注释不会自动触发它们的操作 - 就像Spring中的许多内容一样,该功能必须以声明方式启用(这意味着如果您怀疑缓存是责任,您可以通过删除来禁用它只有一个配置行而不是代码中的所有注释。

要启用缓存注释,请将注释添加@EnableCaching到其中一个 @Configuration类:

@Configuration
@EnableCaching
public class AppConfig {
}

或者,对于XML配置,您可以使用以下cache:annotation-driven元素:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

        <cache:annotation-driven/>
</beans>

无论是cache:annotation-driven元素和@EnableCaching注解让你指定了影响缓存行为被添加到通过AOP应用程序的方式的各种选项。配置有意类似于 @Transactional。

处理缓存注释的默认建议模式是proxy,它允许仅通过代理拦截调用。同一类中的本地调用不能以这种方式截获。对于更高级的拦截模式,请考虑aspectj结合编译时或加载时编织切换到模式。

有关实现所需的高级自定义(使用Java配置)的更多详细信息CachingConfigurer,请参阅 javadoc。

Table 12. Cache annotation settings

方法可见性和缓存注释
使用代理时,应仅将缓存注释应用于具有公共可见性的方法。如果使用这些注释注释protected,private或package-visible方法,则不会引发错误,但带注释的方法不会显示已配置的缓存设置。如果需要注释非公共方法,请考虑使用AspectJ(请参阅本节的其余部分),因为它会更改字节码本身。

Spring建议您只使用注释来注释具体类(以及具体类的方法)@Cache*,而不是注释接口。您当然可以将@Cache*注释放在接口(或接口方法)上,但这只能在您使用基于接口的代理时按预期工作。Java注释不是从接口继承的事实意味着,如果使用基于类的代理(proxy-target-class=“true”)或基于编织的方面(mode=“aspectj”),代理和编织基础结构无法识别缓存设置,并且对象未被包装在缓存代理中,这将是非常糟糕的。

在代理模式(默认)下,只拦截通过代理进入的外部方法调用。这意味着自调用(实际上是目标对象中调用目标对象的另一个方法的方法)在运行时不会导致实际缓存,即使调用的方法被标记@Cacheable。aspectj在这种情况下考虑使用模式。此外,必须完全初始化代理以提供预期的行为,因此您不应该在初始化代码中依赖此功能(即,@PostConstruct)。

8.2.7 使用自定义注释

自定义注释和AspectJ
此功能仅适用于基于代理的方法,但可以通过使用AspectJ进行一些额外的工作。
spring-aspects模块仅定义标准注释的方面。如果您已定义自己的注释,则还需要为这些注释定义方面。检查 AnnotationCacheAspect一个例子。

缓存抽象允许您使用自己的注释来标识触发缓存填充或驱逐的方法。这作为一种模板机制非常方便,因为它消除了复制缓存注释声明的需要,如果指定了键或条件或者org.springframework代码库中不允许使用外部import(),这将特别有用。类似于其余 刻板印象注释,就可以使用@Cacheable,@CachePut, @CacheEvict,和@CacheConfig为元注释 (即,注释,可以注释其他注释)。在以下示例中,我们@Cacheable使用自己的自定义注释替换公共 声明:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface SlowService {
}

在前面的示例中,我们定义了自己的SlowService注释,它本身用注释 @Cacheable。现在我们可以替换以下代码:

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

以下示例显示了自定义注释,我们可以使用它来替换前面的代码:

@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

即使@SlowService不是Spring注释,容器也会在运行时自动获取其声明并理解其含义。请注意, 如前所述,需要启用注释驱动的行为。

8.3 JCache(JSR-107)注释

自从4.1版本,缓存抽象完全支持的JCache标准注释:@CacheResult,@CachePut,@CacheRemove,和 @CacheRemoveAll还有@CacheDefaults,@CacheKey和@CacheValue 同伴。您可以使用这些注释而无需将缓存存储迁移到JSR-107。内部实现使用Spring的缓存抽象,并提供符合规范的默认CacheResolver和KeyGenerator实现。换句话说,如果您已经在使用Spring的缓存抽象,则可以切换到这些标准注释,而无需更改缓存存储(或配置,就此而言)。

8.3.1 功能摘要

对于那些熟悉Spring的缓存注释的人,下表描述了Spring注释与JSR-107版本之间的主要区别:

spring JSR-107 备注
@Cacheable @CacheResult 非常相似。@CacheResult无论缓存的内容如何,​​都可以缓存特定的异常并强制执行该方法。
@CachePut @CachePut 当Spring使用方法调用的结果更新缓存时,JCache要求将其作为带有注释的参数传递@CacheValue。由于这种差异,JCache允许在实际方法调用之前或之后更新缓存。
@CacheEvict @CacheRemove 非常相似。@CacheRemove当方法调用导致异常时,支持条件驱逐。
@CacheEvict(allEntries=true) @CacheRemoveAll 清缓存
@CacheConfig @CacheDefaults 类似

JCache的概念javax.cache.annotation.CacheResolver与Spring的CacheResolver界面完全相同,只是JCache只支持单个缓存。默认情况下,一个简单的实现根据注释上声明的名称检索要使用的缓存。应该注意的是,如果在注释上没有指定缓存名称,则会自动生成默认值。有关@CacheResult#cacheName()更多信息,请参阅javadoc 。

cacheresolver实例由cacheresolver工厂检索。可以为每个缓存操作自定义工厂,如下示例所示:

@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) 
public Book findBook(ISBN isbn)

对于所有引用的类,Spring尝试查找具有给定类型的bean。如果存在多个匹配项,则会创建一个新实例,并且可以使用常规bean生命周期回调,例如依赖项注入。

密钥由一个javax.cache.annotation.CacheKeyGenerator与Spring相同的目的生成KeyGenerator。默认情况下,除非至少使用一个参数注释,否则将考虑所有方法参数@CacheKey。这类似于Spring的自定义密钥生成声明。例如,以下是相同的操作,一个使用Spring的抽象,另一个使用JCache:

Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@CacheResult(cacheName="books")
public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed)

您还可以指定CacheKeyResolver操作,类似于您可以指定的操作CacheResolverFactory。

JCache可以管理带注释的方法抛出的异常。这可以防止更新缓存,但它也可以将异常缓存为失败的指示器,而不是再次调用该方法。假设InvalidIsbnNotFoundException如果ISBN的结构无效则抛出。这是一个永久性的失败(没有用这样的参数检索书籍)。以下缓存异常,以便使用相同的无效ISBN进一步调用直接抛出缓存的异常,而不是再次调用该方法:

@CacheResult(cacheName="books", exceptionCacheName="failures"
            cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)

8.3.2 启用JSR-107支持

除了Spring的声明性注释支持之外,您无需执行任何特定的操作来启用JSR-107支持。如果JSR-107 API和模块都存在于类路径中,则两者@EnableCaching和 cache:annotation-driven元素都会自动启用JCache支持spring-context-support。

除了Spring的声明性注释支持之外,您无需执行任何特定的操作来启用JSR-107支持。如果JSR-107 API和spring-context-support模块都存在于类路径中,则两者@EnableCaching和 cache:annotation-driven元素都会自动启用JCache支持。

根据您的使用情况,选择基本上是您的。您甚至可以在某些服务上使用JSR-107 API并在其他服务上使用Spring自己的注释来混合和匹配服务。但是,如果这些服务影响相同的缓存,则应使用一致且相同的密钥生成实现。

8.4 声明性的基于XML的缓存

8.5 配置缓存存储

缓存抽象提供了多种存储集成选项。要使用它们,您需要声明一个适当的CacheManager(控制和管理Cache实例的实体,并且可以用于检索这些实例以进行存储)。

8.5.1 JDK ConcurrentMap-based Cache

基于JDK的Cache实现位于org.springframework.cache.concurrent包下 。它允许您ConcurrentHashMap 用作后备Cache存储。以下示例显示如何配置两个缓存:

<!-- simple cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <set>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
        </set>
    </property>
</bean>

前面的代码段使用SimpleCacheManager为两个名为Default和Books的嵌套ConcurrentMapCache实例创建一个CacheManager。请注意,这些名称是为每个缓存直接配置的。

由于缓存是由应用程序创建的,所以它被绑定到其生命周期,使其适合于基本用例、测试或简单应用程序。缓存的扩展性很好,速度非常快,但它不提供任何管理、持久性功能或逐出契约。

8.5.2 基于Ehcache的缓存

Ehcache 3.x完全符合JSR-107标准,不需要专门的支持。

Ehcache 2.x实现位于org.springframework.cache.ehcache包中。同样,要使用它,您需要声明适当的CacheManager。以下示例显示了如何执行此操作:

<bean id="cacheManager"
        class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>

<!-- EhCache library setup -->
<bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>

这个设置引导Spring IoC内部的ehcache库(通过ehcachebean),然后连接到专用CacheManager实现。请注意,将读取整个特定于ehcache的配置ehcache.xml。

8.5.3 Caffeine Cache

Caffeine是Java 8重写的Guava缓存,它的实现位于 org.springframework.cache.caffeine包中,可以访问Caffeine的几个功能。

以下示例配置根据CacheManager需要创建缓存的示例:

<bean id="cacheManager"
        class="org.springframework.cache.caffeine.CaffeineCacheManager"/>

您还可以提供明确使用的缓存。在这种情况下,只有manager可以提供。以下示例显示了如何执行此操作:

<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
    <property name="caches">
        <set>
            <value>default</value>
            <value>books</value>
        </set>
    </property>
</bean>

Caffeine CacheManager也支持定制Caffeine和CacheLoader。有关这些内容的详细信息,请参阅Caffeine文档。

8.5.4 基于GemFire的缓存

GemFire是一种面向内存,磁盘支持,弹性可伸缩,持续可用,活动(具有内置的基于模式的订阅通知),全局复制的数据库,并提供全功能的边缘缓存。有关如何将GemFire用作CacheManager(以及更多)的更多信息,请参阅 Spring Data GemFire参考文档。

8.5.5 JSR-107缓存

Spring的缓存抽象也可以使用符合JSR-107的缓存。JCache实现位于org.springframework.cache.jcache包中。

同样,要使用它,您需要声明适当的CacheManager。以下示例显示了如何执行此操作:

<bean id="cacheManager"
        class="org.springframework.cache.jcache.JCacheCacheManager"
        p:cache-manager-ref="jCacheManager"/>

<!-- JSR-107 cache manager setup  -->
<bean id="jCacheManager" .../>

8.5.6 在没有支持存储的情况下处理缓存

有时,在切换环境或进行测试时,您可能会在没有配置实际的后备缓存的情况下进行缓存声明。由于这是无效配置,因此在运行时抛出异常,因为缓存基础结构无法找到合适的存储。在这种情况下,而不是删除缓存声明(这可能证明是乏味的),您可以连接一个不执行缓存的简单虚拟缓存 - 也就是说,它强制每次都执行缓存的方法。以下示例显示了如何执行此操作:

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <list>
            <ref bean="jdkCache"/>
            <ref bean="gemfireCache"/>
        </list>
    </property>
    <property name="fallbackToNoOpCache" value="true"/>
</bean>

CompositeCacheManager在前面的链多个CacheManager实例,并通过fallbackToNoOpCache标志,增加了对不被配置的高速缓存管理器处理的所有定义无操作缓存。也就是说,每一个高速缓存定义未在任一发现jdkCache或gemfireCache(先前在示例配置)由无操作的高速缓存,这不存储任何信息处理,使每次要执行的目标方法。

8.6 插入不同的后端缓存

8.7 如何设置TTL / TTI /驱逐政策/ XXX功能?

发布了43 篇原创文章 · 获赞 3 · 访问量 7763

猜你喜欢

转载自blog.csdn.net/u013523089/article/details/88844770
今日推荐