1072 lines
47 KiB
Plaintext
1072 lines
47 KiB
Plaintext
[[cache]]
|
|
= Cache Abstraction
|
|
|
|
Since version 3.1, the Spring Framework provides support for transparently adding caching to
|
|
an existing Spring application. Similar to the <<data-access.adoc#transaction, transaction>>
|
|
support, the caching abstraction allows consistent use of various caching solutions with
|
|
minimal impact on the code.
|
|
|
|
In Spring Framework 4.1, the cache abstraction was significantly extended with support
|
|
for <<cache-jsr-107,JSR-107 annotations>> and more customization options.
|
|
|
|
|
|
|
|
[[cache-strategies]]
|
|
== Understanding the Cache Abstraction
|
|
|
|
.Cache vs Buffer
|
|
****
|
|
|
|
The terms, "`buffer`" and "`cache,`" tend to be used interchangeably. Note, however,
|
|
that they represent different things. Traditionally, a buffer is used as an intermediate
|
|
temporary store for data between a fast and a slow entity. As one party would have to wait
|
|
for the other (which affects performance), the buffer alleviates this by allowing entire
|
|
blocks of data to move at once rather than in small chunks. The data is written and read
|
|
only once from the buffer. Furthermore, the buffers are visible to at least one party
|
|
that is aware of it.
|
|
|
|
A cache, on the other hand, is, by definition, hidden, and neither party is aware that
|
|
caching occurs. It also improves performance but does so by letting the same data be
|
|
read multiple times in a fast fashion.
|
|
|
|
You can find a further explanation of the differences between a buffer and a cache
|
|
https://en.wikipedia.org/wiki/Cache_(computing)#The_difference_between_buffer_and_cache[here].
|
|
****
|
|
|
|
At its core, the cache abstraction applies caching to Java methods, thus reducing the
|
|
number of executions based on the information available in the cache. That is, each time
|
|
a targeted method is invoked, the abstraction applies a caching behavior that checks
|
|
whether the method has been already invoked for the given arguments. If it has been
|
|
invoked, the cached result is returned without having to invoke the actual method.
|
|
If the method has not been invoked, then it is invoked, and the result is 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 invoked only
|
|
once for a given set of parameters and the result reused without having to actually
|
|
invoke the method again. The caching logic is applied transparently without any
|
|
interference to the invoker.
|
|
|
|
IMPORTANT: 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 they are invoked.
|
|
|
|
The caching abstraction provides other cache-related operations, such as the ability
|
|
to update the content of the cache or to remove one or all entries. These are useful if
|
|
the cache deals with data that can change during the course of the application.
|
|
|
|
As with other services in the Spring Framework, the caching service is an abstraction
|
|
(not a cache implementation) and requires the use of actual storage to store the cache data --
|
|
that is, the abstraction frees you from having to write the caching logic but does not
|
|
provide the actual data store. This abstraction is materialized by the
|
|
`org.springframework.cache.Cache` and `org.springframework.cache.CacheManager` interfaces.
|
|
|
|
Spring provides <<cache-store-configuration, a few implementations>> of that abstraction:
|
|
JDK `java.util.concurrent.ConcurrentMap` based caches, Gemfire cache,
|
|
https://github.com/ben-manes/caffeine/wiki[Caffeine], and JSR-107 compliant caches (such
|
|
as Ehcache 3.x). See <<cache-plug>> for more information on plugging in other cache
|
|
stores and providers.
|
|
|
|
IMPORTANT: The caching abstraction has no special handling for multi-threaded and
|
|
multi-process environments, as such features are handled by the cache implementation.
|
|
|
|
If you have a multi-process environment (that is, an application deployed on several nodes),
|
|
you need to configure your cache provider accordingly. Depending on your use cases, a copy
|
|
of the same data on several nodes can be enough. However, if you change the data during
|
|
the course of the application, you may need to enable other propagation mechanisms.
|
|
|
|
Caching a particular item is a direct equivalent of the typical
|
|
get-if-not-found-then-proceed-and-put-eventually code blocks
|
|
found with programmatic cache interaction.
|
|
No locks are applied, and several threads may try to load the same item concurrently.
|
|
The same applies to eviction. If several threads are trying to update or evict data
|
|
concurrently, you may use stale data. Certain cache providers offer advanced features
|
|
in that area. See the documentation of your cache provider for more details.
|
|
|
|
To use the cache abstraction, you need to take care of two aspects:
|
|
|
|
* Caching declaration: Identify the methods that need to be cached and their policies.
|
|
* Cache configuration: The backing cache where the data is stored and from which it is read.
|
|
|
|
|
|
|
|
[[cache-annotations]]
|
|
== Declarative Annotation-based Caching
|
|
|
|
For caching declaration, Spring's caching 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.
|
|
|
|
|
|
[[cache-annotations-cacheable]]
|
|
=== The `@Cacheable` Annotation
|
|
|
|
As the name implies, you can use `@Cacheable` to demarcate methods that are cacheable --
|
|
that is, methods for which the result is stored in the cache so that, on subsequent
|
|
invocations (with the same arguments), the value in the cache is returned without
|
|
having to actually invoke the method. In its simplest form, the annotation declaration
|
|
requires the name of the cache associated with the annotated method, as the following
|
|
example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable("books")
|
|
public Book findBook(ISBN isbn) {...}
|
|
----
|
|
|
|
In the preceding snippet, the `findBook` method is associated with the cache named `books`.
|
|
Each time the method is called, the cache is checked to see whether the invocation has
|
|
already been run and does not have to be repeated. While in most cases, only one
|
|
cache is declared, the annotation lets multiple names be specified so that more than one
|
|
cache is being used. In this case, each of the caches is checked before invoking the
|
|
method -- if at least one cache is hit, the associated value is returned.
|
|
|
|
NOTE: All the other caches that do not contain the value are also updated, even though
|
|
the cached method was not actually invoked.
|
|
|
|
The following example uses `@Cacheable` on the `findBook` method with multiple caches:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable({"books", "isbns"})
|
|
public Book findBook(ISBN isbn) {...}
|
|
----
|
|
|
|
[[cache-annotations-cacheable-default-key]]
|
|
==== 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. The caching abstraction
|
|
uses a simple `KeyGenerator` based on the following algorithm:
|
|
|
|
* If no parameters are given, return `SimpleKey.EMPTY`.
|
|
* If only one parameter is given, return that instance.
|
|
* If more than one parameter is given, return a `SimpleKey` that contains 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,
|
|
you need to change the strategy.
|
|
|
|
To provide a different default key generator, you need to implement the
|
|
`org.springframework.cache.interceptor.KeyGenerator` interface.
|
|
|
|
[NOTE]
|
|
====
|
|
The default key generation strategy changed with the release of Spring 4.0. Earlier
|
|
versions of Spring used a key generation strategy that, for multiple key parameters,
|
|
considered only the `hashCode()` of parameters and not `equals()`. This could cause
|
|
unexpected key collisions (see https://jira.spring.io/browse/SPR-10237[SPR-10237]
|
|
for background). The new `SimpleKeyGenerator` uses a compound key for such scenarios.
|
|
|
|
If you want to keep using the previous key strategy, you can configure the deprecated
|
|
`org.springframework.cache.interceptor.DefaultKeyGenerator` class or create a custom
|
|
hash-based `KeyGenerator` implementation.
|
|
====
|
|
|
|
[[cache-annotations-cacheable-key]]
|
|
==== Custom Key Generation Declaration
|
|
|
|
Since caching is generic, the target methods are quite likely to have various signatures
|
|
that cannot be readily 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). Consider the following example:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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. Furthermore, what if only one of the two is important
|
|
while the other is not?
|
|
|
|
For such cases, the `@Cacheable` annotation lets you specify how the key is generated
|
|
through its `key` attribute. You can use <<core.adoc#expressions, 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
|
|
<<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 works for all methods.
|
|
|
|
The following examples use various SpEL declarations (if you are not familiar with SpEL,
|
|
do yourself a favor and read <<core.adoc#expressions, Spring Expression Language>>):
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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)
|
|
----
|
|
|
|
The preceding snippets show how easy it is to select a certain argument, one of its
|
|
properties, or even an arbitrary (static) method.
|
|
|
|
If the algorithm responsible for generating the key is too specific or if it needs
|
|
to be shared, you can define a custom `keyGenerator` on the operation. To do so,
|
|
specify the name of the `KeyGenerator` bean implementation to use, as the following
|
|
example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
|
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
|
|
----
|
|
|
|
NOTE: The `key` and `keyGenerator` parameters are mutually exclusive and an operation
|
|
that specifies both results in an exception.
|
|
|
|
[[cache-annotations-cacheable-default-cache-resolver]]
|
|
==== Default Cache Resolution
|
|
|
|
The caching abstraction uses a simple `CacheResolver` that
|
|
retrieves the caches defined at the operation level by using the configured
|
|
`CacheManager`.
|
|
|
|
To provide a different default cache resolver, you need to implement the
|
|
`org.springframework.cache.interceptor.CacheResolver` interface.
|
|
|
|
[[cache-annotations-cacheable-cache-resolver]]
|
|
==== Custom Cache Resolution
|
|
|
|
The default cache resolution fits well for applications that work with a
|
|
single `CacheManager` and have no complex cache resolution requirements.
|
|
|
|
For applications that work with several cache managers, you can set the
|
|
`cacheManager` to use for each operation, as the following example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") <1>
|
|
public Book findBook(ISBN isbn) {...}
|
|
----
|
|
<1> Specifying `anotherCacheManager`.
|
|
|
|
|
|
You can also replace the `CacheResolver` entirely in a fashion similar to that of
|
|
replacing <<cache-annotations-cacheable-key, key generation>>. The resolution is
|
|
requested for every cache operation, letting the implementation actually resolve
|
|
the caches to use based on runtime arguments. The following example shows how to
|
|
specify a `CacheResolver`:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheResolver="runtimeCacheResolver") <1>
|
|
public Book findBook(ISBN isbn) {...}
|
|
----
|
|
<1> Specifying the `CacheResolver`.
|
|
|
|
|
|
[NOTE]
|
|
====
|
|
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
|
|
results in an exception, as a custom `CacheManager` is ignored by the
|
|
`CacheResolver` implementation. This is probably not what you expect.
|
|
====
|
|
|
|
[[cache-annotations-cacheable-synchronized]]
|
|
==== Synchronized Caching
|
|
|
|
In a multi-threaded environment, certain operations might be concurrently invoked for
|
|
the same argument (typically on startup). By default, the cache abstraction does not
|
|
lock anything, and the same value may be computed several times, defeating the purpose
|
|
of caching.
|
|
|
|
For those particular cases, you can use the `sync` attribute to instruct the underlying
|
|
cache provider to lock the cache entry while the value is being computed. As a result,
|
|
only one thread is busy computing the value, while the others are blocked until the entry
|
|
is updated in the cache. The following example shows how to use the `sync` attribute:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="foos", sync=true) <1>
|
|
public Foo executeExpensiveOperation(String id) {...}
|
|
----
|
|
<1> Using the `sync` attribute.
|
|
|
|
NOTE: This is an optional feature, and your favorite cache library may not support it.
|
|
All `CacheManager` implementations provided by the core framework support it. See the
|
|
documentation of your cache provider for more details.
|
|
|
|
[[cache-annotations-cacheable-condition]]
|
|
==== Conditional Caching
|
|
|
|
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 use cases through the
|
|
`condition` 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, the method is invoked every time no matter what values are in the cache
|
|
or what arguments are used). For example, the following method is cached only if the
|
|
argument `name` has a length shorter than 32:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="book", condition="#name.length() < 32") <1>
|
|
public Book findBook(String name)
|
|
----
|
|
<1> Setting a condition on `@Cacheable`.
|
|
|
|
|
|
In addition to the `condition` parameter, you can use the `unless` parameter to veto the
|
|
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated
|
|
after the method has been invoked. To expand on the previous example, perhaps we only
|
|
want to cache paperback books, as the following example does:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") <1>
|
|
public Book findBook(String name)
|
|
----
|
|
<1> Using the `unless` attribute to block hardbacks.
|
|
|
|
|
|
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
|
|
is _present_, it will be stored in the associated cache. If an `Optional` value is not
|
|
present, `null` will be stored in the associated cache. `#result` always refers to the
|
|
business entity and never a supported wrapper, so the previous example can be rewritten
|
|
as follows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
|
|
public Optional<Book> findBook(String name)
|
|
----
|
|
|
|
Note that `#result` still refers to `Book` and not `Optional<Book>`. Since it might be
|
|
`null`, we use SpEL's <<core.adoc#expressions-operator-safe-navigation, safe navigation operator>>.
|
|
|
|
[[cache-spel-context]]
|
|
==== Available Caching SpEL Evaluation Context
|
|
|
|
Each `SpEL` expression evaluates against a dedicated <<core.adoc#expressions-language-ref, `context`>>.
|
|
In addition to the built-in parameters, the framework provides dedicated caching-related
|
|
metadata, such as the argument names. The following table describes the items made
|
|
available to the context so that you can use them for key and conditional computations:
|
|
|
|
[[cache-spel-context-tbl]]
|
|
.Cache SpEL available metadata
|
|
|===
|
|
| Name| Location| Description| Example
|
|
|
|
| `methodName`
|
|
| Root object
|
|
| The name of the method being invoked
|
|
| `#root.methodName`
|
|
|
|
| `method`
|
|
| Root object
|
|
| The method being invoked
|
|
| `#root.method.name`
|
|
|
|
| `target`
|
|
| Root object
|
|
| The target object being invoked
|
|
| `#root.target`
|
|
|
|
| `targetClass`
|
|
| Root object
|
|
| The class of the target being invoked
|
|
| `#root.targetClass`
|
|
|
|
| `args`
|
|
| Root object
|
|
| The arguments (as array) used for invoking the target
|
|
| `#root.args[0]`
|
|
|
|
| `caches`
|
|
| Root object
|
|
| Collection of caches against which the current method is run
|
|
| `#root.caches[0].name`
|
|
|
|
| Argument name
|
|
| Evaluation context
|
|
| Name of any of the method arguments. If the names are not available
|
|
(perhaps due to having no debug information), the argument names are also available under the `#a<#arg>`
|
|
where `#arg` stands for the argument index (starting from `0`).
|
|
| `#iban` or `#a0` (you can also use `#p0` or `#p<#arg>` notation as an alias).
|
|
|
|
| `result`
|
|
| Evaluation context
|
|
| The result of the method call (the value to be cached). Only available in `unless`
|
|
expressions, `cache put` expressions (to compute the `key`), or `cache evict`
|
|
expressions (when `beforeInvocation` is `false`). For supported wrappers (such as
|
|
`Optional`), `#result` refers to the actual object, not the wrapper.
|
|
| `#result`
|
|
|===
|
|
|
|
|
|
[[cache-annotations-put]]
|
|
=== The `@CachePut` Annotation
|
|
|
|
When the cache needs to be updated without interfering with the method execution,
|
|
you can use the `@CachePut` annotation. That is, the method is always invoked and its
|
|
result is placed into the cache (according to the `@CachePut` options). It supports
|
|
the same options as `@Cacheable` and should be used for cache population rather than
|
|
method flow optimization. The following example uses the `@CachePut` annotation:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@CachePut(cacheNames="book", key="#isbn")
|
|
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
|
|
----
|
|
|
|
IMPORTANT: Using `@CachePut` and `@Cacheable` annotations on the same method is generally
|
|
strongly discouraged because they have different behaviors. While the latter causes the
|
|
method invocation to be skipped by using the cache, the former forces the invocation in
|
|
order to run 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. Note also that such conditions should not rely
|
|
on the result object (that is, the `#result` variable), as these are validated up-front to
|
|
confirm the exclusion.
|
|
|
|
|
|
[[cache-annotations-evict]]
|
|
=== The `@CacheEvict` annotation
|
|
|
|
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. As opposed to
|
|
`@Cacheable`, `@CacheEvict` demarcates methods that perform cache
|
|
eviction (that is, methods that act as triggers for removing data from the cache).
|
|
Similarly to its sibling, `@CacheEvict` requires specifying one or more caches
|
|
that are affected by the action, allows a custom cache and key resolution or a
|
|
condition to be specified, and features an extra parameter
|
|
(`allEntries`) that indicates whether a cache-wide eviction needs to be performed
|
|
rather than just an entry eviction (based on the key). The following example evicts
|
|
all entries from the `books` cache:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@CacheEvict(cacheNames="books", allEntries=true) <1>
|
|
public void loadBooks(InputStream batch)
|
|
----
|
|
<1> Using the `allEntries` attribute to evict all entries from the cache.
|
|
|
|
This option comes in handy when an entire cache region needs to be cleared out.
|
|
Rather than evicting each entry (which would take a long time, since it is inefficient),
|
|
all the entries are removed in one operation, as the preceding example shows.
|
|
Note that the framework ignores any key specified in this scenario as it does not apply
|
|
(the entire cache is evicted, not only one entry).
|
|
|
|
You can also indicate whether the eviction should occur after (the default) or before
|
|
the method is invoked by using the `beforeInvocation` attribute. The former provides the
|
|
same semantics as the rest of the annotations: Once the method completes successfully,
|
|
an action (in this case, eviction) on the cache is run. If the method does not
|
|
run (as it might be cached) or an exception is thrown, the eviction does not occur.
|
|
The latter (`beforeInvocation=true`) causes the eviction to always occur before the
|
|
method is invoked. This is useful in cases where the eviction does not need to be tied
|
|
to the method outcome.
|
|
|
|
Note that `void` methods can be used with `@CacheEvict` - as the methods act as a
|
|
trigger, the return values are ignored (as they do not interact with the cache). This is
|
|
not the case with `@Cacheable` which adds data to the cache or updates data in the cache
|
|
and, thus, requires a result.
|
|
|
|
|
|
[[cache-annotations-caching]]
|
|
=== The `@Caching` Annotation
|
|
|
|
Sometimes, multiple annotations of the same type (such as `@CacheEvict` or
|
|
`@CachePut`) need to be specified -- for example, because the condition or the key
|
|
expression is different between different caches. `@Caching` lets multiple nested
|
|
`@Cacheable`, `@CachePut`, and `@CacheEvict` annotations be used on the same method.
|
|
The following example uses two `@CacheEvict` annotations:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
|
|
public Book importBooks(String deposit, Date date)
|
|
----
|
|
|
|
|
|
[[cache-annotations-config]]
|
|
=== The `@CacheConfig` annotation
|
|
|
|
So far, we have seen that caching operations offer many customization options and that
|
|
you can set these options for each operation. 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 can be replaced by a single class-level definition. This is where `@CacheConfig`
|
|
comes into play. The following examples uses `@CacheConfig` to set the name of the cache:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@CacheConfig("books") <1>
|
|
public class BookRepositoryImpl implements BookRepository {
|
|
|
|
@Cacheable
|
|
public Book findBook(ISBN isbn) {...}
|
|
}
|
|
----
|
|
<1> Using `@CacheConfig` to set the name of the cache.
|
|
|
|
`@CacheConfig` is a class-level annotation that allows sharing the cache names,
|
|
the custom `KeyGenerator`, the custom `CacheManager`, and the custom `CacheResolver`.
|
|
Placing this annotation on the class does not turn on any caching operation.
|
|
|
|
An operation-level customization always overrides a customization set on `@CacheConfig`.
|
|
Therefore, this gives three levels of customizations for each cache operation:
|
|
|
|
* Globally configured, available for `CacheManager`, `KeyGenerator`.
|
|
* At the class level, using `@CacheConfig`.
|
|
* At the operation level.
|
|
|
|
|
|
[[cache-annotation-enable]]
|
|
=== Enabling 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 than all the annotations in
|
|
your code).
|
|
|
|
To enable caching annotations add the annotation `@EnableCaching` to one of your
|
|
`@Configuration` classes:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableCaching
|
|
public class AppConfig {
|
|
}
|
|
----
|
|
|
|
Alternatively, for XML configuration you can use the `cache:annotation-driven` element:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<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 https://www.springframework.org/schema/beans/spring-beans.xsd
|
|
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
|
|
|
|
<cache:annotation-driven/>
|
|
</beans>
|
|
----
|
|
|
|
Both the `cache:annotation-driven` element and the `@EnableCaching` annotation let you
|
|
specify various options that influence the way the caching behavior is added to the
|
|
application through AOP. The configuration is intentionally similar with that of
|
|
<<data-access.adoc#tx-annotation-driven-settings, `@Transactional`>>.
|
|
|
|
NOTE: The default advice mode for processing caching annotations is `proxy`, which allows
|
|
for interception of calls through the proxy only. Local calls within the same class
|
|
cannot get intercepted that way. For a more advanced mode of interception, consider
|
|
switching to `aspectj` mode in combination with compile-time or load-time weaving.
|
|
|
|
NOTE: For more detail about advanced customizations (using Java configuration) that are
|
|
required to implement `CachingConfigurer`, see the
|
|
{api-spring-framework}/cache/annotation/CachingConfigurer.html[javadoc].
|
|
|
|
[[cache-annotation-driven-settings]]
|
|
.Cache annotation settings
|
|
[cols="1,1,1,3"]
|
|
|===
|
|
| XML Attribute | Annotation Attribute | Default | Description
|
|
|
|
| `cache-manager`
|
|
| N/A (see the {api-spring-framework}/cache/annotation/CachingConfigurer.html[`CachingConfigurer`] javadoc)
|
|
| `cacheManager`
|
|
| The name of the cache manager to use. A default `CacheResolver` is initialized behind
|
|
the scenes with this cache manager (or `cacheManager` if not set). For more
|
|
fine-grained management of the cache resolution, consider setting the 'cache-resolver'
|
|
attribute.
|
|
|
|
| `cache-resolver`
|
|
| N/A (see the {api-spring-framework}/cache/annotation/CachingConfigurer.html[`CachingConfigurer`] javadoc)
|
|
| A `SimpleCacheResolver` using the configured `cacheManager`.
|
|
| The bean name of the CacheResolver that is to be used to resolve the backing caches.
|
|
This attribute is not required and needs to be specified only as an alternative to
|
|
the 'cache-manager' attribute.
|
|
|
|
| `key-generator`
|
|
| N/A (see the {api-spring-framework}/cache/annotation/CachingConfigurer.html[`CachingConfigurer`] javadoc)
|
|
| `SimpleKeyGenerator`
|
|
| Name of the custom key generator to use.
|
|
|
|
| `error-handler`
|
|
| N/A (see the {api-spring-framework}/cache/annotation/CachingConfigurer.html[`CachingConfigurer`] javadoc)
|
|
| `SimpleCacheErrorHandler`
|
|
| The name of the custom cache error handler to use. By default, any exception thrown during
|
|
a cache related operation is thrown back at the client.
|
|
|
|
| `mode`
|
|
| `mode`
|
|
| `proxy`
|
|
| The default mode (`proxy`) processes annotated beans to be proxied by using Spring's AOP
|
|
framework (following proxy semantics, as discussed earlier, applying to method calls
|
|
coming in through the proxy only). The alternative mode (`aspectj`) instead weaves the
|
|
affected classes with Spring's AspectJ caching 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
|
|
<<core.adoc#aop-aj-ltw-spring, Spring configuration>> for details on how to set up
|
|
load-time weaving.)
|
|
|
|
| `proxy-target-class`
|
|
| `proxyTargetClass`
|
|
| `false`
|
|
| Applies to proxy mode only. Controls what type of caching proxies are created for
|
|
classes annotated with the `@Cacheable` or `@CacheEvict` annotations. If the
|
|
`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 <<core.adoc#aop-proxying, Proxying Mechanisms>>
|
|
for a detailed examination of the different proxy types.)
|
|
|
|
| `order`
|
|
| `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 AOP advice, see <<core.adoc#aop-ataspectj-advice-ordering, Advice Ordering>>.)
|
|
No specified ordering means that the AOP subsystem determines the order of the advice.
|
|
|===
|
|
|
|
NOTE: `<cache:annotation-driven/>` looks for `@Cacheable/@CachePut/@CacheEvict/@Caching`
|
|
only on beans in the same application context in which it is defined. This means that,
|
|
if you put `<cache:annotation-driven/>` in a `WebApplicationContext` for a
|
|
`DispatcherServlet`, it checks for beans only in your controllers, not your services.
|
|
See <<web.adoc#mvc-servlet, the MVC section>> for more information.
|
|
|
|
.Method visibility and cache annotations
|
|
****
|
|
When you use 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 using AspectJ (see the rest of this section)
|
|
if you need to annotate non-public methods, as it changes the bytecode itself.
|
|
****
|
|
|
|
TIP: Spring recommends that you only annotate concrete classes (and methods of concrete
|
|
classes) with the `@Cache{asterisk}` annotations, as opposed to annotating interfaces.
|
|
You certainly can place an `@Cache{asterisk}` annotation on an interface (or an interface
|
|
method), but this works only if you use the proxy mode (`mode="proxy"`). If you use the
|
|
weaving-based aspect (`mode="aspectj"`), the caching settings are not recognized on
|
|
interface-level declarations by the weaving infrastructure.
|
|
|
|
NOTE: In proxy mode (the default), only external method calls coming in through the
|
|
proxy are intercepted. This means that self-invocation (in effect, a method within the
|
|
target object that calls another method of the target object) does not lead to actual
|
|
caching at runtime even if the invoked method is marked with `@Cacheable`. Consider
|
|
using the `aspectj` mode in this case. Also, the proxy must be fully initialized to
|
|
provide the expected behavior, so you should not rely on this feature in your
|
|
initialization code (that is, `@PostConstruct`).
|
|
|
|
|
|
[[cache-annotation-stereotype]]
|
|
=== Using Custom Annotations
|
|
|
|
.Custom annotation and AspectJ
|
|
****
|
|
This feature works only with the proxy-based approach but can be enabled
|
|
with a bit of extra effort by using AspectJ.
|
|
|
|
The `spring-aspects` module defines an aspect for the standard annotations only.
|
|
If you have defined your own annotations, you also need to define an aspect for
|
|
those. Check `AnnotationCacheAspect` for an example.
|
|
****
|
|
|
|
The caching abstraction lets you use your own annotations to identify what method
|
|
triggers cache population or eviction. This is quite handy as a template mechanism,
|
|
as it eliminates the need to duplicate cache annotation declarations, which is
|
|
especially useful if the key or condition are specified or if the foreign imports
|
|
(`org.springframework`) are not allowed in your code base. Similarly to the rest
|
|
of the <<core.adoc#beans-stereotype-annotations, stereotype>> annotations, you can
|
|
use `@Cacheable`, `@CachePut`, `@CacheEvict`, and `@CacheConfig` as
|
|
<<core.adoc#beans-meta-annotations, meta-annotations>> (that is, annotations that
|
|
can annotate other annotations). In the following example, we replace a common
|
|
`@Cacheable` declaration with our own custom annotation:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@Target({ElementType.METHOD})
|
|
@Cacheable(cacheNames="books", key="#isbn")
|
|
public @interface SlowService {
|
|
}
|
|
----
|
|
|
|
In the preceding example, we have defined our own `SlowService` annotation,
|
|
which itself is annotated with `@Cacheable`. Now we can replace the following code:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Cacheable(cacheNames="books", key="#isbn")
|
|
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
|
|
----
|
|
|
|
The following example shows the custom annotation with which we can replace the
|
|
preceding code:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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 mentioned
|
|
<<cache-annotation-enable, earlier>>, annotation-driven behavior needs to be enabled.
|
|
|
|
|
|
|
|
[[cache-jsr-107]]
|
|
== JCache (JSR-107) Annotations
|
|
|
|
Since version 4.1, Spring's caching abstraction fully supports the JCache standard
|
|
(JSR-107) annotations: `@CacheResult`, `@CachePut`, `@CacheRemove`, and `@CacheRemoveAll`
|
|
as well as the `@CacheDefaults`, `@CacheKey`, and `@CacheValue` companions.
|
|
You can use these annotations even 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]]
|
|
=== Feature Summary
|
|
|
|
For those who are familiar with Spring's caching annotations, the following table
|
|
describes the main differences between the Spring annotations and their JSR-107
|
|
counterparts:
|
|
|
|
.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 that it be passed it as an argument that is annotated with `@CacheValue`.
|
|
Due to this difference, JCache allows updating the cache before or after the
|
|
actual method invocation.
|
|
|
|
| `@CacheEvict`
|
|
| `@CacheRemove`
|
|
| Fairly similar. `@CacheRemove` supports conditional eviction when the
|
|
method invocation results in an exception.
|
|
|
|
| `@CacheEvict(allEntries=true)`
|
|
| `@CacheRemoveAll`
|
|
| See `@CacheRemove`.
|
|
|
|
| `@CacheConfig`
|
|
| `@CacheDefaults`
|
|
| Lets you configure the same concepts, in a similar fashion.
|
|
|===
|
|
|
|
JCache has the notion of `javax.cache.annotation.CacheResolver`, which is identical
|
|
to the Spring's `CacheResolver` interface, except that JCache supports only 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. See the javadoc
|
|
of `@CacheResult#cacheName()` for more information.
|
|
|
|
`CacheResolver` instances are retrieved by a `CacheResolverFactory`. It is possible
|
|
to customize the factory for each cache operation, as the following example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) <1>
|
|
public Book findBook(ISBN isbn)
|
|
----
|
|
<1> Customizing the factory for this operation.
|
|
|
|
NOTE: For all referenced classes, Spring tries to locate a bean with the given type.
|
|
If more than one match exists, a new instance is created and can use the regular
|
|
bean lifecycle callbacks, such as dependency injection.
|
|
|
|
Keys are generated by a `javax.cache.annotation.CacheKeyGenerator` that serves the
|
|
same purpose as Spring's `KeyGenerator`. By default, all method arguments are taken
|
|
into account, unless at least one parameter is annotated with `@CacheKey`. This is
|
|
similar to Spring's <<cache-annotations-cacheable-key, custom key generation
|
|
declaration>>. For instance, the following are identical operations, one using
|
|
Spring's abstraction and the other using JCache:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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)
|
|
----
|
|
|
|
You can also specify the `CacheKeyResolver` on the operation, similar to how you can
|
|
specify the `CacheResolverFactory`.
|
|
|
|
JCache can manage exceptions thrown by annotated methods. This can prevent an update of
|
|
the cache, but it can also cache the exception as an indicator of the failure instead of
|
|
calling the method again. Assume that `InvalidIsbnNotFoundException` is thrown if the
|
|
structure of the ISBN is invalid. This is a permanent failure (no book could ever be
|
|
retrieved with such a parameter). The following caches the exception so that further
|
|
calls with the same, invalid, ISBN throw the cached exception directly instead of
|
|
invoking the method again:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@CacheResult(cacheName="books", exceptionCacheName="failures"
|
|
cachedExceptions = InvalidIsbnNotFoundException.class)
|
|
public Book findBook(ISBN isbn)
|
|
----
|
|
|
|
|
|
[[enabling-jsr-107-support]]
|
|
=== Enabling JSR-107 Support
|
|
|
|
You do not need to do anything specific to enable the JSR-107 support alongside Spring's
|
|
declarative annotation support. Both `@EnableCaching` and the `cache:annotation-driven`
|
|
XML element automatically enable the JCache support if both the JSR-107 API and the
|
|
`spring-context-support` module are present in the classpath.
|
|
|
|
NOTE: Depending on your use case, the choice is basically yours. You can even mix and
|
|
match services by using the JSR-107 API on some and using Spring's own annotations on
|
|
others. However, if these services impact the same caches, you should use a consistent
|
|
and identical key generation implementation.
|
|
|
|
|
|
|
|
[[cache-declarative-xml]]
|
|
== Declarative XML-based Caching
|
|
|
|
If annotations are not an option (perhaps due to having no access to the sources
|
|
or no external code), you can use XML for declarative caching. So, instead of
|
|
annotating the methods for caching, you can specify the target method and the
|
|
caching directives externally (similar to the declarative transaction management
|
|
<<data-access.adoc#transaction-declarative-first-example, advice>>). The example
|
|
from the previous section can be translated into the following example:
|
|
|
|
[source,xml,indent=0]
|
|
[subs="verbatim"]
|
|
----
|
|
<!-- the service we want to make cacheable -->
|
|
<bean id="bookService" class="x.y.service.DefaultBookService"/>
|
|
|
|
<!-- cache definitions -->
|
|
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
|
|
<cache:caching cache="books">
|
|
<cache:cacheable method="findBook" key="#isbn"/>
|
|
<cache:cache-evict method="loadBooks" all-entries="true"/>
|
|
</cache:caching>
|
|
</cache:advice>
|
|
|
|
<!-- apply the cacheable behavior to all BookService interfaces -->
|
|
<aop:config>
|
|
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.BookService.*(..))"/>
|
|
</aop:config>
|
|
|
|
<!-- cache manager definition omitted -->
|
|
----
|
|
|
|
In the preceding configuration, the `bookService` is made cacheable. The caching semantics
|
|
to apply are encapsulated in the `cache:advice` definition, which causes the `findBooks`
|
|
method to be used for putting data into the cache and the `loadBooks` method for evicting
|
|
data. Both definitions work against the `books` cache.
|
|
|
|
The `aop:config` definition applies the cache advice to the appropriate points in the
|
|
program by using the AspectJ pointcut expression (more information is available in
|
|
<<core.adoc#aop, Aspect Oriented Programming with Spring>>). In the preceding example,
|
|
all methods from the `BookService` are considered and the cache advice is applied to them.
|
|
|
|
The declarative XML caching supports all of the annotation-based model, so moving between
|
|
the two should be fairly easy. Furthermore, both can be used inside the same application.
|
|
The XML-based approach does not touch the target code. However, it is inherently more
|
|
verbose. When dealing with classes that have overloaded methods that are targeted for
|
|
caching, identifying the proper methods does take an extra effort, since the `method`
|
|
argument is not a good discriminator. In these cases, you can use the AspectJ pointcut
|
|
to cherry pick the target methods and apply the appropriate caching functionality.
|
|
However, through XML, it is easier to apply package or group or interface-wide caching
|
|
(again, due to the AspectJ pointcut) and to create template-like definitions (as we did
|
|
in the preceding example by defining the target cache through the `cache:definitions`
|
|
`cache` attribute).
|
|
|
|
|
|
|
|
[[cache-store-configuration]]
|
|
== Configuring the Cache Storage
|
|
|
|
The cache abstraction provides several storage integration options. To use them, you need
|
|
to declare an appropriate `CacheManager` (an entity that controls and manages `Cache`
|
|
instances and that can be used to retrieve these for storage).
|
|
|
|
|
|
[[cache-store-configuration-jdk]]
|
|
=== JDK `ConcurrentMap`-based Cache
|
|
|
|
The JDK-based `Cache` implementation resides under
|
|
`org.springframework.cache.concurrent` package. It lets you use `ConcurrentHashMap`
|
|
as a backing `Cache` store. The following example shows how to configure two caches:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<!-- 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>
|
|
----
|
|
|
|
The preceding snippet uses the `SimpleCacheManager` to create a `CacheManager` for the
|
|
two nested `ConcurrentMapCache` instances 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, persistence capabilities,
|
|
or eviction contracts.
|
|
|
|
|
|
[[cache-store-configuration-eviction]]
|
|
=== Ehcache-based Cache
|
|
|
|
Ehcache 3.x is fully JSR-107 compliant and no dedicated support is required for it. See
|
|
<<cache-store-configuration-jsr107>> for details.
|
|
|
|
|
|
[[cache-store-configuration-caffeine]]
|
|
=== Caffeine Cache
|
|
|
|
Caffeine is a Java 8 rewrite of Guava's cache, and its implementation is located in the
|
|
`org.springframework.cache.caffeine` package and provides access to several features
|
|
of Caffeine.
|
|
|
|
The following example configures a `CacheManager` that creates the cache on demand:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<bean id="cacheManager"
|
|
class="org.springframework.cache.caffeine.CaffeineCacheManager"/>
|
|
----
|
|
|
|
You can also provide the caches to use explicitly. In that case, only those
|
|
are made available by the manager. The following example shows how to do so:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
|
|
<property name="cacheNames">
|
|
<set>
|
|
<value>default</value>
|
|
<value>books</value>
|
|
</set>
|
|
</property>
|
|
</bean>
|
|
----
|
|
|
|
The Caffeine `CacheManager` also supports custom `Caffeine` and `CacheLoader`.
|
|
See the https://github.com/ben-manes/caffeine/wiki[Caffeine documentation]
|
|
for more information about those.
|
|
|
|
|
|
[[cache-store-configuration-gemfire]]
|
|
=== GemFire-based Cache
|
|
|
|
GemFire is a memory-oriented, disk-backed, elastically scalable, continuously available,
|
|
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), see the
|
|
{docs-spring-gemfire}/html/[Spring Data GemFire reference documentation].
|
|
|
|
|
|
[[cache-store-configuration-jsr107]]
|
|
=== JSR-107 Cache
|
|
|
|
Spring's caching abstraction can also use JSR-107-compliant caches. The JCache
|
|
implementation is located in the `org.springframework.cache.jcache` package.
|
|
|
|
Again, to use it, you need to declare the appropriate `CacheManager`.
|
|
The following example shows how to do so:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<bean id="cacheManager"
|
|
class="org.springframework.cache.jcache.JCacheCacheManager"
|
|
p:cache-manager-ref="jCacheManager"/>
|
|
|
|
<!-- JSR-107 cache manager setup -->
|
|
<bean id="jCacheManager" .../>
|
|
----
|
|
|
|
|
|
[[cache-store-configuration-noop]]
|
|
=== Dealing with Caches without a Backing Store
|
|
|
|
Sometimes, when switching environments or doing testing, you might have cache
|
|
declarations without having an actual backing cache configured. As this is an invalid
|
|
configuration, an exception is thrown at runtime, since the caching infrastructure
|
|
is unable to find a suitable store. In situations like this, rather than removing the
|
|
cache declarations (which can prove tedious), you can wire in a simple dummy cache that
|
|
performs no caching -- that is, it forces the cached methods to be invoked every time.
|
|
The following example shows how to do so:
|
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
<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>
|
|
----
|
|
|
|
The `CompositeCacheManager` in the preceding chains multiple `CacheManager` instances and,
|
|
through the `fallbackToNoOpCache` flag, adds a no-op cache for all the definitions not
|
|
handled by the configured cache managers. That is, every cache definition not found in
|
|
either `jdkCache` or `gemfireCache` (configured earlier in the example) is handled by
|
|
the no-op cache, which does not store any information, causing the target method to be
|
|
invoked every time.
|
|
|
|
|
|
|
|
[[cache-plug]]
|
|
== Plugging-in Different Back-end Caches
|
|
|
|
Clearly, there are plenty of caching products out there that you can use as a backing
|
|
store. For those that do not support JSR-107 you need to provide a `CacheManager` and a
|
|
`Cache` implementation. This may sound harder than it is, since, in practice, the classes
|
|
tend to be simple https://en.wikipedia.org/wiki/Adapter_pattern[adapters] that map the
|
|
caching abstraction framework on top of the storage API, as the _Caffeine_ classes do.
|
|
Most `CacheManager` classes can use the classes in the
|
|
`org.springframework.cache.support` package (such as `AbstractCacheManager` which takes
|
|
care of the boiler-plate code, leaving only the actual mapping to be completed).
|
|
|
|
|
|
|
|
[[cache-specific-config]]
|
|
== How can I Set the TTL/TTI/Eviction policy/XXX feature?
|
|
|
|
Directly through your cache provider. The cache abstraction is an abstraction,
|
|
not a cache implementation. The solution you use might support various data
|
|
policies and different topologies that other solutions do not support (for example,
|
|
the JDK `ConcurrentHashMap` -- exposing that in the cache abstraction would be useless
|
|
because there would no backing support). Such functionality should be controlled
|
|
directly through the backing cache (when configuring it) or through its native API.
|
|
|