声明,下面翻译均使用google翻译,非本人自己翻译(没那本事),又不正确的地方凑合看,只为交流学习使用。原文连接:http://docs.spring.io/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html#cache-spel-context
27. Cache Abstraction
27.1 Introduction
27.1介绍
Since version 3.1, Spring Framework provides support for transparently adding caching into an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.
从3.1版本开始,Spring Framework提供支持将缓存透明地添加到现有的Spring应用程序中。 与事务支持类似,缓存抽象允许对各种缓存解决方案的一致使用,对代码的影响最小。
27.2 Understanding the cache abstraction
27.2了解缓存抽象
At its core, the abstraction applies caching to Java methods, reducing thus the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction will apply a caching behaviour checking whether the method has been already executed for the given arguments. If it has, then the cached result is returned without having to execute the actual method; if it has not, then method is executed, the result cached and returned to the user so that, the next time the method is invoked, the cached result is returned. This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result reused without having to actually execute the method again. The caching logic is applied transparently without any interference to the invoker.
在其核心,抽象将缓存应用于Java方法,从而减少基于缓存中可用信息的执行次数。 也就是说,每次调用目标方法时,抽象将应用一个缓存行为,检查该方法是否已经针对给定的参数执行。 如果它有,则返回缓存的结果,而不必执行实际的方法; 如果没有,则执行方法,将结果缓存并返回给用户,以便下次调用该方法时,返回缓存的结果。 这样,对于给定的一组参数,昂贵的方法(无论是CPU还是IO绑定)只能执行一次,并且结果被重用,而不必再次实际执行该方法。 缓存逻辑被透明地应用,而没有对调用者的任何干扰。
*Important
Obviously this approach works only for methods that are guaranteed to return the same output (result) for a given input (or arguments) no matter how many times it is being executed.*
重要
显然,这种方法只适用于保证为给定输入(或参数)返回相同输出(结果)的方法,无论它执行多少次。
要使用缓存抽象,开发人员需要考虑两个方面:
- 缓存声明 - 标识需要缓存的方法及其策略
- 缓存配置 - 存储和读取数据的后备缓存
Note that just like other services in Spring Framework, the caching service is an abstraction (not a cache implementation) and requires the use of an actual storage to store the cache data - that is, the abstraction frees the developer from having to write the caching logic but does not provide the actual stores. There are two integrations available out of the box, for JDK java.util.concurrent.ConcurrentMap and Ehcache - see Section 27.5, “Plugging-in different back-end caches” for more information on plugging in other cache stores/providers.
注意,就像Spring Framework中的其他服务一样,缓存服务是一个抽象(而不是缓存实现),并且需要使用实际的存储来存储缓存数据 - 也就是说,抽象使开发人员不必编写缓存 逻辑,但不提供实际的商店。 JDK java.util.concurrent.ConcurrentMap和Ehcache有两种可用的集成方式 - 有关插入其他缓存商店/提供程序的更多信息,请参见第27.5节“插入不同的后端缓存”。
27.3 Declarative annotation-based caching
27.3基于声明式注释的缓存
For caching declaration, the abstraction provides two Java annotations: @Cacheable and @CacheEvict which allow methods to trigger cache population or cache eviction. Let us take a closer look at each annotation:
对于缓存声明,抽象提供了两个Java注释:@Cacheable和@CacheEvict,它们允许方法触发缓存填充或缓存驱逐。 让我们仔细看看每个注释:
27.3.1 @Cacheable annotation
27.3.1 @Cacheable注释
As the name implies, @Cacheable is used to demarcate methods that are cacheable - that is, methods for whom the result is stored into the cache so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method. In its simplest form, the annotation declaration requires the name of the cache associated with the annotated method:
顾名思义,@Cacheable用于区分可缓存的方法 - 即将结果存储到缓存中的方法,以便在后续调用(使用相同的参数)时,返回缓存中的值,而不必返回 实际执行该方法。 在其最简单的形式中,注释声明需要与注释方法相关联的缓存的名称:
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
In the snippet above, the method findBook is associated with the cache named books. Each time the method is called, the cache is checked to see whether the invocation has been already executed and does not have to be repeated. While in most cases, only one cache is declared, the annotation allows multiple names to be specified so that more then one cache are being used. In this case, each of the caches will be checked before executing the method - if at least one cache is hit, then the associated value will be returned:
在上面的代码段中,findBook方法与名为books的缓存相关联。 每次调用该方法时,都会检查缓存以查看调用是否已执行,不必重复。 虽然在大多数情况下,只声明一个缓存,注释允许指定多个名称,以便使用多于一个缓存。 在这种情况下,在执行方法之前将检查每个高速缓存 - 如果至少有一个高速缓存被命中,则将返回关联的值:
Note
All the other caches that do not contain the method will be updated as well event though the cached method was not actually executed.
注意
不包含该方法的所有其他缓存将被更新以及事件,虽然缓存的方法没有被实际执行。
@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}
27.3.1.1 Default Key Generation
Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for cache access. Out of the box, the caching abstraction uses a simple hash-code based KeyGenerator that computes the key based on the hashes of all objects used for method invocation. This approach works well for objects with natural keys as long as the hashCode() reflects that. If that is not the case then for distributed or persistent environments, the strategy needs to be changed as the objects hashCode is not preserved. In fact, depending on the JVM implementation or running conditions, the same hashCode can be reused for different objects, in the same VM instance.
由于缓存本质上是键值存储,因此缓存方法的每次调用都需要转换为缓存访问的合适密钥。 开箱即用的缓存抽象使用一个简单的基于哈希码的KeyGenerator,它基于用于方法调用的所有对象的哈希计算密钥。 这种方法对于具有自然键的对象很有效,只要hashCode()反映了这一点。 如果不是这样,那么对于分布式或持久性环境,策略需要更改为不保留对象hashCode。 事实上,根据JVM实现或运行条件,相同的hashCode可以在同一个VM实例中重复用于不同的对象。
To provide a different default key generator, one needs to implement the org.springframework.cache.KeyGenerator interface. Once configured, the generator will be used for each declaration that doesn not specify its own key generation strategy (see below).
要提供不同的默认密钥生成器,需要实现org.springframework.cache.KeyGenerator接口。 一旦配置,生成器将用于没有指定其自己的密钥生成策略的每个声明(见下文)。
27.3.1.2 Custom Key Generation Declaration
27.3.1.2自定义密钥生成声明
Since caching is generic, it is quite likely the target methods have various signatures that cannot be simply mapped on top of the cache structure. This tends to become obvious when the target method has multiple arguments out of which only some are suitable for caching (while the rest are used only by the method logic). For example:
因为缓存是通用的,所以很可能目标方法具有不能简单地映射在缓存结构之上的各种签名。 当目标方法具有多个参数时,这些变量变得明显,其中只有一些适合于缓存(而其余的仅仅由方法逻辑使用)。 例如:
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed
At first glance, while the two boolean arguments influence the way the book is found, they are no use for the cache. Further more what if only one of the two is important while the other is not?
乍一看,虽然这两个布尔参数影响了book的发现方式,但它们对缓存没有用处。 更进一步,如果只有一个是重要的,而另一个不是?
For such cases, the @Cacheable annotation allows the user to specify how the key is generated through its key attribute. The developer can use SpEL to pick the arguments of interest (or their nested properties), perform operations or even invoke arbitrary methods without having to write any code or implement any interface. This is the recommended approach over the default generator since methods tend to be quite different in signatures as the code base grows; while the default strategy might work for some methods, it rarely does for all methods.
对于这种情况,@Cacheable注释允许用户指定如何通过其键属性生成密钥。 开发人员可以使用SpEL选择感兴趣的参数(或它们的嵌套属性),执行操作,甚至调用任意方法,而不必编写任何代码或实现任何接口。 这是对默认生成器的推荐方法,因为方法趋向于在签名中随着代码库增长而截然不同; 而默认策略可能适用于某些方法,它很少适用于所有方法。
Below are some examples of various SpEL declarations - if you are not familiar with it, do yourself a favour and read Chapter 6, Spring Expression Language (SpEL):
下面是一些Spel声明的例子 - 如果你不熟悉它,请自己一个忙,阅读第6章,Spring表达式语言(Spel):
@Cacheable(value="book", key="isbn"
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="book", key="isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(value="book", key="T(someType).hash(isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
The snippets above, show how easy it is to select a certain argument, one of its properties or even an arbitrary (static) method.
上面的代码段显示了选择某个参数,其属性之一,甚至任意(静态)方法是多么容易。
27.3.1.3 Conditional caching
27.3.1.3条件高速缓存
Sometimes, a method might not be suitable for caching all the time (for example, it might depend on the given arguments). The cache annotations support such functionality through the conditional parameter which takes a SpEL expression that is evaluated to either true or false. If true, the method is cached - if not, it behaves as if the method is not cached, that is executed every since time no matter what values are in the cache or what arguments are used. A quick example - the following method will be cached, only if the argument name has a length shorter then 32:
有时,一个方法可能不适合所有的时间缓存(例如,它可能取决于给定的参数)。 缓存注释通过条件参数支持这样的功能,该条件参数采用被评估为真或假的SpEL表达式。 如果为true,那么该方法将被缓存 - 如果没有,它的行为就好像该方法没有被缓存一样,无论缓存中有什么值或使用什么参数,都会执行该方法。 一个快速示例 - 以下方法将被缓存,只有当参数名称的长度小于32时:
@Cacheable(value="book", condition="name.length < 32")
public Book findBook(String name)
27.3.1.4 Available caching SpEL evaluation context
27.3.1.4可用的高速缓存SpEL评估上下文
Each SpEL expression evaluates again a dedicated context. In addition to the build in parameters, the framework provides dedicated caching related metadata such as the argument names. The next table lists the items made available to the context so one can use them for key and conditional(see next section) computations:
每个SpEL表达式再次评估专用上下文。 除了在参数中构建外,框架还提供专用的缓存相关元数据,例如参数名称。 下表列出了可用于上下文的项目,因此可以将它们用于键和条件(见下一节)计算:
Table 27.1. Cache SpEL available metadata
Name | Location | Description | Example |
---|---|---|---|
methodName | root object | The name of the method being invoked | root.methodName |
caches | root object | Collection of caches against which the current method is executed | root.caches[0].name |
parameter name | evaluation context | Name of any of the method parameter. If for some reason the names are not available (ex: no debug information), the parameter names are also available under the p<#arg> where #arg stands for the parameter index (starting from 0). | iban or p0 |
27.3.2 @CacheEvict annotation
27.3.2 @CacheEvict注释
The cache abstraction allows not just population of a cache store but also eviction. This process is useful for removing stale or unused data from the cache. Opposed to @Cacheable, annotation @CacheEvict demarcates methods that perform cache eviction, that is methods that act as triggers for removing data from the cache. Just like its sibling, @CacheEvict requires one to specify one (or multiple) caches that are affected by the action, allows a key or a condition to be specified but in addition, features an extra parameter allEntries which indicates whether a cache-wide eviction needs to be performed rather then just an entry one (based on the key):
缓存抽象不仅允许缓存存储的填充,而且允许逐出。 此过程对从缓存中删除过期或未使用的数据非常有用。 与@Cacheable相反,注释@CacheEvict划分了执行缓存逐出的方法,即作为从缓存中删除数据的触发器的方法。 就像它的兄弟,@CacheEvict需要一个指定受操作影响的一个(或多个)缓存,允许指定键或条件,但另外还有一个额外的参数allEntries,它指示是否缓存宽的驱逐 需要执行,而不是仅仅输入一(基于键):
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch)
This option comes in handy when an entire cache region needs to be cleared out - rather then evicting each entry (which would take a long time since it is inefficient), all the entires are removed in one operation as shown above. Note that the framework will ignore any key specified in this scenario as it does not apply (the entire cache is evicted not just one entry).
当整个高速缓存区域需要被清除时,该选项很方便,而不是逐出每个条目(由于其效率低,需要很长时间),所以在一个操作中去除所有的入口,如上所示。 请注意,框架将忽略此场景中指定的任何键,因为它不适用(整个高速缓存不仅仅是逐出的)。
It is important to note that void methods can be used with @CacheEvict - as the methods act as triggers, the return values are ignored (as they don’t interact with the cache) - this is not the case with @Cacheable which adds/update data into the cache and thus requires a result.
重要的是要注意,void方法可以与@CacheEvict一起使用 - 因为方法充当触发器,返回值被忽略(因为它们不与缓存交互) - @Cacheable不是这样的情况, 将数据更新到高速缓存中,因此需要结果。
27.3.3 Enable caching annotations
27.3.3启用缓存注释
It is important to note that even though declaring the cache annotations does not automatically triggers their actions - like many things in Spring, the feature has to be declaratively enabled (which means if you ever suspect caching is to blame, you can disable it by removing only one configuration line rather then all the annotations in your code). In practice, this translates to one line that informs Spring that it should process the cache annotations, namely:
重要的是要注意,即使声明缓存注释不会自动触发他们的操作 - 像Spring中的很多东西一样,该特性必须声明性地启用(这意味着如果你怀疑缓存是怪的,你可以通过删除 只有一个配置行,而不是你的代码中的所有注释)。 实际上,这转换为一行,通知Spring应该处理缓存注释,即:
<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.xsdhttp://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />
The namespace allows various options to be specified that influence the way the caching behaviour is added to the application through AOP. The configuration is similar (on purpose) with that of tx:annotation-driven:
命名空间允许指定影响缓存行为通过AOP添加到应用程序的方式的各种选项。 配置与tx的配置类似(有意):annotation-driven:
Attribute | Default | Description | 中文翻译 |
---|---|---|---|
cache-manager | cacheManager | Name of cache manager to use. Only required if the name of the cache manager is not cacheManager, as in the example above. | cacheManager |
mode | proxy | The default mode “proxy” processes annotated beans to be proxied using Spring’s AOP framework (following proxy semantics, as discussed above, applying to method calls coming in through the proxy only). The alternative mode “aspectj” instead weaves the affected classes with Spring’s AspectJ transaction aspect, modifying the target class byte code to apply to any kind of method call. AspectJ weaving requires spring-aspects.jar in the classpath as well as load-time weaving (or compile-time weaving) enabled. (See Section 7.8.4.5, “Spring configuration” for details on how to set up load-time weaving.) | 默认模式“代理”使用Spring的AOP框架(下面的代理语义,如上所述,应用于仅通过代理进入的方法调用)处理要被代理的带注释的bean。 替代模式“aspectj”改为用Spring的AspectJ事务方面编辑受影响的类,修改目标类字节码以应用于任何类型的方法调用。 AspectJ编织需要在类路径中使用spring-aspects.jar以及启用加载时编织(或编译时编织)。 (有关如何设置加载时编织的详细信息,请参见第7.8.4.5节“弹簧配置”。 |
proxy-target-class | false | Applies to proxy mode only. Controls what type of transactional proxies are created for classes annotated with the @Cacheable or @CacheEvict annotations. If the proxy-target-class attribute is set to true, then class-based proxies are created. If proxy-target-class is false or if the attribute is omitted, then standard JDK interface-based proxies are created. (See Section 7.6, “Proxying mechanisms” for a detailed examination of the different proxy types.) | 仅适用于代理模式。 控制对使用@Cacheable或@CacheEvict注释注释的类创建哪种类型的事务代理。 如果proxy-target-class属性设置为true,那么将创建基于类的代理。 如果proxy-target-class为false或如果省略该属性,那么将创建基于标准JDK接口的代理。 (有关不同代理类型的详细检查,请参见第7.6节“代理机制”。) |
order | Ordered.LOWEST_PRECEDENCE | Defines the order of the cache advice that is applied to beans annotated with @Cacheable or @CacheEvict. (For more information about the rules related to ordering of AOP advice, see Section 7.2.4.7, “Advice ordering”.) No specified ordering means that the AOP subsystem determines the order of the advice. | 定义应用于使用@Cacheable或@CacheEvict注释的bean的缓存通知的顺序。 (有关与AOP建议的排序相关的规则的更多信息,请参见第7.2.4.7节“建议排序”。)没有指定的排序意味着AOP子系统确定建议的顺序。 |
Note
The proxy-target-class attribute on the element controls what type of caching proxies are created for classes annotated with the @Cacheable/@CacheEvict annotation. If proxy-target-class attribute is set to true, class-based proxies are created. If proxy-target-class is false or if the attribute is omitted, standard JDK interface-based proxies are created. (See Section 7.6, “Proxying mechanisms” for a discussion of the different proxy types.)
注意
27.3.4 Using custom annotations
27.3.4使用自定义注释
The caching abstraction allows one to use her own annotations to identify what method trigger cache population or eviction. This is quite handy as a template mechanism as it eliminates the need to duplicate cache annotation declarations (especially useful if the key or condition are specified) or if the foreign imports (org.springframework) are not allowed in your code base. Similar to the rest of the stereotype annotations, both @Cacheable and @CacheEvict can be used as meta-annotations, that is annotations that can annotate other annotations. To wit, let us replace a common @Cacheable declaration with our own, custom annotation:
缓存抽象允许使用她自己的注释来识别什么方法触发缓存填充或驱逐。 这是一个非常方便的模板机制,因为它消除了重复缓存注释声明(如果指定键或条件时特别有用)或者如果外部导入(org.springframework)不允许在您的代码库中。 与其他构造型注释类似,@Cacheable和@CacheEvict都可以用作元注释,即可注释其他注释的注释。 也就是说,我们用我们自己的自定义注释来替换一个公共的@Cacheable声明:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(value=“books”, key="isbn")
public @interface SlowService {
}
Above, we have defined our own SlowService annotation which itself is annotated with @Cacheable - now we can replace the following code:
上面,我们定义了我们自己的SlowService注释,它本身用@Cacheable注释 - 现在我们可以替换以下代码:
@Cacheable(value="books", key="isbn"
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
with:
@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Even though @SlowService is not a Spring annotation, the container automatically picks up its declaration at runtime and understands its meaning. Note that as mentined above, the annotation-driven behaviour needs to be enabled.
即使@SlowService不是一个Spring注释,容器会在运行时自动选择其声明,并理解其含义。 注意,如上所述,需要启用注释驱动的行为。
27.4 Configuring the cache storage
27.4配置缓存存储
Out of the box, the cache abstraction provides integration with two storages - one on top of the JDK ConcurrentMap and one for ehcache library. To use them, one needs to simply declare an appropriate CacheManager - an entity that controls and manages Caches and can be used to retrieve these for storage.
开箱即用的缓存抽象提供了与两个存储集成 - 一个在JDK ConcurrentMap之上,一个用于ehcache库。 要使用它们,需要简单地声明一个合适的CacheManager - 一个控制和管理缓存的实体,可以用来检索这些缓存用于存储。
27.4.1 JDK ConcurrentMap-based Cache
27.4.1基于JDK并发映射的缓存
The JDK-based Cache implementation resides under org.springframework.cache.concurrent package. It allows one to use ConcurrentHashMap as a backing Cache store.
基于JDK的Cache实现驻留在org.springframework.cache.concurrent包下。 它允许使用ConcurrentHashMap作为后台高速缓存存储。
<!-- generic cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="books"/>
</set>
</property>
</bean>
The snippet above uses the SimpleCacheManager to create a CacheManager for the two, nested Concurrent Cache implementations named default and books. Note that the names are configured directly for each cache.
As the cache is created by the application, it is bound to its lifecycle, making it suitable for basic use cases, tests or simple applications. The cache scales well and is very fast but it does not provide any management or persistence capabilities nor eviction contracts.
上面的代码段使用SimpleCacheManager为两个嵌套的并发缓存实现(名为default和books)创建一个CacheManager。 请注意,为每个缓存直接配置名称。
由于缓存由应用程序创建,因此它被绑定到其生命周期,使其适合于基本用例,测试或简单应用程序。 缓存扩展良好,速度非常快,但它不提供任何管理或持久性能力或驱逐合同。
27.4.2 Ehcache-based Cache
27.4.2基于Ehcache的缓存
The Ehcache implementation is located under org.springframework.cache.ehcache package. Again, to use it, one simply needs to declare the appropriate CacheManager:
Ehcache实现位于org.springframework.cache.ehcache包下。 再次,要使用它,只需要声明适当的CacheManager:
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhcacheCacheManager" p:cache-manager="ehcache"/>
<!-- Ehcache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>
This setup bootstraps ehcache library inside Spring IoC (through bean ehcache) which is then wired into the dedicated CacheManager implementation. Note the entire ehcache-specific configuration is read from the resource ehcache.xml.
这个设置引导Spring IoC内的ehcache库(通过bean ehcache),然后连接到专用的CacheManager实现。 请注意,从资源ehcache.xml中读取整个特定于ehcache的配置。
27.5 Plugging-in different back-end caches
27.5插入不同的后端高速缓存
Clearly there are plenty of caching products out there that can be used as a backing store. To plug them in, one needs to provide a CacheManager and Cache implementation since unfortunately there is no available standard that we can use instead. This may sound harder then it is since in practice, the classes tend to be simple adapters that map the caching abstraction framework on top of the storage API as the ehcache classes can show. Most CacheManager classes can use the classes in org.springframework.cache.support package, such as AbstractCacheManager which takes care of the boiler-plate code leaving only the actual mapping to be completed. We hope that in time, the libraries that provide integration with Spring can fill in this small configuration gap.
显然有很多缓存产品可以用作后台存储。 要插入他们,需要提供一个CacheManager和Cache实现,因为不幸的是没有可用的标准,我们可以改用。 这可能听起来更难,因为在实践中,类往往是简单的适配器,将缓存抽象框架映射到存储API的顶部,因为ehcache类可以显示。 大多数CacheManager类可以使用org.springframework.cache.support包中的类,例如AbstractCacheManager,它负责处理样板代码,只留下实际的映射。 我们希望,及时提供与Spring集成的库可以填补这个小的配置空缺。
(完)