This commit is a general update of the Cache Abstraction chapter.
Existing sections have been updated with recent improvements made in
that area, in particular:
* Guava and JSR-107 caches support
* New @CacheConfig annotation allowing to share some key customizations
at class-level
* CacheResolver interface used to resolve the cache(s) to use at
runtime
* Update section on @CachePut
This commit also describes the support of standard JCache annotations,
i.e. JSR-107.
Issues: SPR-11490, SPR-11316, SPR-10629, SPR-9616, SPR-8696
@ -46089,6 +46089,8 @@ into an existing Spring application. Similar to the <<transaction,transaction>>
@@ -46089,6 +46089,8 @@ into an existing Spring application. Similar to the <<transaction,transaction>>
the caching abstraction allows consistent use of various caching solutions with minimal
impact on the code.
As from Spring 4.1, the cache abstraction has been significantly improved with the
support of <<cache-jsr-107,JSR-107 annotations>> and more customization options.
@ -46132,29 +46134,41 @@ output (result) for a given input (or arguments) no matter how many times it is
@@ -46132,29 +46134,41 @@ output (result) for a given input (or arguments) no matter how many times it is
executed.
====
To use the cache abstraction, the developer needs to take care of two aspects:
Other cache-related operations are provided by the abstraction such as the ability
to update the content of the cache or remove one of all entries. These are useful if
the cache deals with data that can change during the course of the application.
* caching declaration - identify the methods that need to be cached and their policy
* cache configuration - the backing cache where the data is stored and read from
Note that just like other services in Spring Framework, the caching service is an
Just like other services in the 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
http://ehcache.org/[EhCache] - see <<cache-plug>> for more information on plugging in
the caching logic but does not provide the actual stores. This abstraction is
materialized by the `org.springframework.cache.Cache` and
There are <<cache-store-configuration,a few implementations>> of that abstraction
available out of the box: JDK `java.util.concurrent.ConcurrentMap` based caches,
http://ehcache.org/[EhCache], Gemfire cache,
https://code.google.com/p/guava-libraries/wiki/CachesExplained[Guava caches] and
JSR-107 compliant caches. See <<cache-plug>> for more information on plugging in
other cache stores/providers.
To use the cache abstraction, the developer needs to take care of two aspects:
* caching declaration - identify the methods that need to be cached and their policy
* cache configuration - the backing cache where the data is stored and read from
[[cache-annotations]]
=== Declarative annotation-based caching
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:
For caching declaration, the abstraction provides a set of Java annotations:
* `@Cacheable` triggers cache population
* `@CacheEvict` triggers cache eviction
* `@CachePut` updates the cache without interfering with the method execution
* `@Caching` regroups multiple cache operations to be applied on a method
* `@CacheConfig` shares some common cache-related settings at class-level
Let us take a closer look at each annotation:
[[cache-annotations-cacheable]]
==== @Cacheable annotation
@ -46176,20 +46190,20 @@ In the snippet above, the method `findBook` is associated with the cache named `
@@ -46176,20 +46190,20 @@ In the snippet above, the method `findBook` is associated with the cache named `
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
than 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:
[NOTE]
====
All the other caches that do not contain the method will be updated as well even though
All the other caches that do not contain the value will be updated as well even though
the cached method was not actually executed.
====
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Cacheable({"books", "isbns"})
@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}
----
@ -46205,13 +46219,11 @@ abstraction uses a simple `KeyGenerator` based on the following algorithm:
@@ -46205,13 +46219,11 @@ abstraction uses a simple `KeyGenerator` based on the following algorithm:
* If more the one param is given, return a `SimpleKey` containing all parameters.
This approach works well for most use-cases; As long as parameters have __natural keys__
and implement valid `hashCode()` and `equals()` methods. If that is not the case the the
and implement valid `hashCode()` and `equals()` methods. If that is not the case then the
strategy needs to be changed.
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 does not specify its own key generation strategy (see
@ -46251,7 +46263,7 @@ generated through its `key` attribute. The developer can use <<expressions,SpEL>
@@ -46251,7 +46263,7 @@ generated through its `key` attribute. The developer can use <<expressions,SpEL>
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
<<cache-annotations-cacheable-default-key,default>> generator since methods tend to be
<<cache-annotations-cacheable-default-key,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.
@ -46274,6 +46286,75 @@ do yourself a favor and read <<expressions>>:
@@ -46274,6 +46286,75 @@ do yourself a favor and read <<expressions>>:
The snippets above show how easy it is to select a certain argument, one of its
properties or even an arbitrary (static) method.
If the algorithm responsible to generate the key is too specific or if it needs
to be shared, you may define a custom `keyGenerator` on the operation. To do
this, specify the name of the `KeyGenerator` bean implementation to use:
Since Spring 4.1, the `value` attribute of the cache annotations are no longer
mandatory since this particular information can be provided by the `CacheResolver`
regardless of the content of the annotation.
Similarly to `key` and `keyGenerator`, the `cacheManager` and `cacheResolver`
parameters are mutually exclusive and an operation specifying both will
result in an exception as a custom `CacheManager` will be ignored by the
`CacheResolver` implementation. This is probably not what you expect.
====
[[cache-annotations-cacheable-condition]]
===== Conditional caching
@ -46288,7 +46369,7 @@ only if the argument `name` has a length shorter than 32:
@@ -46288,7 +46369,7 @@ only if the argument `name` has a length shorter than 32:
@ -46312,7 +46393,7 @@ Each `SpEL` expression evaluates again a dedicated
@@ -46312,7 +46393,7 @@ Each `SpEL` expression evaluates again a dedicated
<<expressions-language-ref,`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:
conditional computations:
[[cache-spel-context-tbl]]
.Cache SpEL available metadata
@ -46364,7 +46445,6 @@ conditional (see next section) computations:
@@ -46364,7 +46445,6 @@ conditional (see next section) computations:
|===
[[cache-annotations-put]]
==== @CachePut annotation
@ -46372,15 +46452,25 @@ For cases where the cache needs to be updated without interfering with the metho
@@ -46372,15 +46452,25 @@ For cases where the cache needs to be updated without interfering with the metho
execution, one can use the `@CachePut` annotation. That is, the method will always be
executed and its result placed into the cache (according to the `@CachePut` options). It
supports the same options as `@Cacheable` and should be used for cache population rather
then method flow optimization.
than method flow optimization:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@CachePut(value="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
----
[IMPORTANT]
====
Note that using `@CachePut` and `@Cacheable` annotations on the same method is generally
discouraged because they have different behaviors. While the latter causes the method
execution to be skipped by using the cache, the former forces the execution in order to
execute a cache update. This leads to unexpected behavior and with the exception of
specific corner-cases (such as annotations having conditions that exclude them from each
other), such declarations should be avoided.
====
[[cache-annotations-evict]]
@ -46391,20 +46481,21 @@ This process is useful for removing stale or unused data from the cache. Opposed
@@ -46391,20 +46481,21 @@ This process is useful for removing stale or unused data from the cache. Opposed
`@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 specifying 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):
that are affected by the action, allows a custom cache and key resolution 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):
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@CacheEvict(value="books", allEntries=true)
@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
entries 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
public Book importBooks(String deposit, Date date)
----
[[cache-annotations-config]]
==== @CacheConfig annotation
So far we have seen that caching operations offered many customization options and
these can be set on an operation basis. However, some of the customization options
can be tedious to configure if they apply to all operations of the class. For
instance, specifying the name of the cache to use for every cache operation of the
class could be replaced by a single class-level definition. This is where `@CacheConfig`
comes into play.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
**@CacheConfig("books")**
public class BookRepositoryImpl implements BookRepository {
@Cacheable
public Book findBook(ISBN isbn) {...}
}
----
`@CacheConfig` is a class-level annotation that allows to share the cache names, the custom
`KeyGenerator`, the custom `CacheManager` and finally the custom `CacheResolver`. Placing
this annotation on the class does not turn on any caching operation.
An operation-level customization will always override a customization set on `@CacheConfig`. This
gives therefore three levels of customizations per cache operation:
* Globally configured, available for `CacheManager`, `KeyGenerator`
* At class level, using `@CacheConfig`
* At the operation level
[[cache-annotation-enable]]
==== Enable caching annotations
It is important to note that even though declaring the cache annotations does not
automatically trigger 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
disable it by removing only one configuration line rather than all the annotations in
your code).
To enable caching annotations add the annotation `@EnableCaching` to one of your
@ -46495,7 +46617,7 @@ application through AOP. The configuration is intentionally similar with that of
@@ -46495,7 +46617,7 @@ application through AOP. The configuration is intentionally similar with that of
| N/A (See `CachingConfigurer` javadocs)
| 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`
| `mode`
@ -46529,16 +46651,16 @@ application through AOP. The configuration is intentionally similar with that of
@@ -46529,16 +46651,16 @@ application through AOP. The configuration is intentionally similar with that of
[NOTE]
====
`<cache:annotation-driven/>` only looks for `@Cacheable/@CacheEvict` on beans in the
same application context it is defined in. This means that, if you put
`<cache:annotation-driven/>` only looks for `@Cacheable/@CachePut/@CacheEvict/@Caching`
on beans in the same application context it is defined in. This means that, if you put
`<cache:annotation-driven/>` in a `WebApplicationContext` for a `DispatcherServlet`, it
only checks for `@Cacheable/@CacheEvict` beans in your controllers, and not your
services. See <<mvc-servlet>> for more information.
only checks for beans in your controllers, and not your services. See <<mvc-servlet>>
for more information.
====
.Method visibility and @Cacheable / @CachePut / @CacheEvict
.Method visibility and cache annotations
****
When using proxies, you should apply the `@Cache*` annotations only to methods with
When using proxies, you should apply the cache annotations only to methods with
__public__ visibility. If you do annotate protected, private or package-visible methods
with these annotations, no error is raised, but the annotated method does not exhibit
the configured caching settings. Consider the use of AspectJ (see below) if you need to
@ -46572,13 +46694,13 @@ using the aspectj mode in this case.
@@ -46572,13 +46694,13 @@ using the aspectj mode in this case.
[[cache-annotation-stereotype]]
==== Using custom annotations
The caching abstraction allows you to use your own annotations to identify what method
trigger cache population or eviction. This is quite handy as a template mechanism as it
triggers 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
<<beans-stereotype-annotations,stereotype>> annotations, both `@Cacheable` and
`@CacheEvict` can be used as <<beans-meta-annotations,meta-annotations>>, that is
annotations that can annotate other annotations. To wit, let us replace a common
`@CacheEvict` and `@CacheConfig` can be used as <<beans-meta-annotations,meta-annotations>>,
that is annotations that can annotate other annotations. To wit, let us replace a common
`@Cacheable` declaration with our own, custom annotation:
[source,java,indent=0]
@ -46615,6 +46737,133 @@ up its declaration at runtime and understands its meaning. Note that as mentione
@@ -46615,6 +46737,133 @@ up its declaration at runtime and understands its meaning. Note that as mentione
<<cache-annotation-enable,above>>, the annotation-driven behavior needs to be enabled.
[[cache-jsr-107]]
=== JCache (JSR-107) annotations
Since the Spring Framework 4.1, the caching abstraction fully supports the JCache
standard annotations: these are `@CacheResult`, `@CacheEvict`, `@CacheRemove` and
`@CacheRemoveAll` as well as the `@CachingDefaults`, `@CacheKey` and `@CacheValue`
companions. These annotations can be used right the way without migrating your
cache store to JSR-107: the internal implementation uses Spring's caching abstraction
and provides default `CacheResolver` and `KeyGenerator` implementations that are
compliant with the specification. In other words, if you are already using Spring's
caching abstraction, you can switch to these standard annotations without changing
your cache storage (or configuration, for that matter).
[[cache-jsr-107-summary]]
==== Features summary
For those who are familiar with Spring's caching annotations, the following table
describes the main differences between the Spring annotations and the JSR-107
counterpart:
.Spring vs. JSR-107 caching annotations
[cols="1,1,3"]
|===
| Spring| JSR-107| Remark
| `@Cacheable`
| `@CacheResult`
| Fairly similar. `@CacheResult` can cache specific exceptions and force the
execution of the method regardless of the content of the cache.
| `@CachePut`
| `@CachePut`
| While Spring updates the cache with the result of the method invocation, JCache
requires to pass it as an argument that is annotated with `@CacheValue`. Due
to this difference, JCache allows to update the cache before or after the
actual method invocation.
| `@CacheEvict`
| `@CacheRemove`
| Fairly similar. `@CacheRemove` supports a conditional evict in case the
method invocation results in an exception.
| `@CacheEvict(allEntries=true)`
| `@CacheRemoveAll`
| See `@CacheRemove`.
| `@CacheConfig`
| `@CacheDefaults`
| Allows to configure the same concepts, in a similar fashion.
|===
JCache has the notion of `javax.cache.annotation.CacheResolver` that is identical
to the Spring's `CacheResolver` interface, except that JCache only supports a single
cache. By default, a simple implementation retrieves the cache to use based on
the name declared on the annotation. It should be noted that if no cache name
is specified on the annotation, a default is automatically generated, check the
javadoc of `@CacheResult#cacheName()` for more information.
`CacheResolver` instances are retrieved by a `CacheResolverFactory`. It is
possible to customize the factory per cache operation:
Nothing specific needs to be done to enable the JSR-107 support alongside Spring's
declarative annotation support. Both `@EnableCaching` and the
`cache:annotation-driven` element will enable automatically the JCache support
if both the JSR-107 API and the `spring-context-support` module are present in
the classpath.
[NOTE]
====
Depending of your use case, the choice is basically yours. You can even mix
and match services using the JSR-107 API and others using Spring's own
annotations. Be aware however that if these services are impacting the same
caches, a consistent and identical key generation implementation should be used.
====
[[cache-declarative-xml]]
@ -46674,13 +46923,11 @@ we did in the example above by defining the target cache through the `cache:defi
@@ -46674,13 +46923,11 @@ we did in the example above by defining the target cache through the `cache:defi
[[cache-store-configuration]]
=== Configuring the cache storage
Out of the box, the cache abstraction provides integration with two storages - one on
top of the JDK `ConcurrentMap` and one for http://ehcache.org/[EhCache] library. To use
Out of the box, the cache abstraction provides several storages integration. To use
them, one needs to simply declare an appropriate `CacheManager` - an entity that
controls and manages ++Cache++s and can be used to retrieve these for storage.
[[cache-store-configuration-jdk]]
==== JDK ConcurrentMap-based Cache
@ -46691,7 +46938,7 @@ as a backing `Cache` store.
@@ -46691,7 +46938,7 @@ as a backing `Cache` store.
@ -46722,17 +46969,52 @@ Again, to use it, one simply needs to declare the appropriate `CacheManager`:
@@ -46722,17 +46969,52 @@ Again, to use it, one simply needs to declare the appropriate `CacheManager`:
The Guava `CacheManager` also supports customs `CacheBuilder` and `CacheLoader`. See
the https://code.google.com/p/guava-libraries/wiki/CachesExplained[Guava documentation]
for more information about those.
[[cache-store-configuration-gemfire]]
==== GemFire-based Cache
@ -46741,9 +47023,27 @@ GemFire is a memory-oriented/disk-backed, elastically scalable, continuously ava
@@ -46741,9 +47023,27 @@ GemFire is a memory-oriented/disk-backed, elastically scalable, continuously ava
active (with built-in pattern-based subscription notifications), globally replicated
database and provides fully-featured edge caching. For further information on how to use
GemFire as a CacheManager (and more), please refer to the