spring-framework/framework-docs/modules/ROOT/pages/integration/cache.adoc

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.