You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
636 lines
28 KiB
636 lines
28 KiB
[[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. |
|
|
|
|
|
|
|
|