Browse Source
+ draft cache documentation git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3829 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
2 changed files with 209 additions and 0 deletions
@ -0,0 +1,198 @@
@@ -0,0 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
||||
|
||||
<chapter id="cache"> |
||||
<title>Cache Abstraction</title> |
||||
|
||||
<section id="cache-introduction"> |
||||
<title>Introduction</title> |
||||
|
||||
<para>Since version 3.1, Spring Framework provides support for transparently |
||||
adding caching into an existing Spring application. Similar to the <link linkend="transaction">transaction</link> |
||||
support, the caching abstraction allows consistent use of various caching |
||||
solutions with minimal impact on the code.</para> |
||||
</section> |
||||
|
||||
<section id="cache-strategies"> |
||||
<title>Understanding the cache abstraction</title> |
||||
|
||||
<sidebar> |
||||
<title>Cache vs Buffer</title> |
||||
<para>The terms "buffer" and "cache" tend to be used interchangeably; note however they represent different things. |
||||
A buffer is used traditionally as an intermediate temporary store for data between a fast and a slow entity. As one |
||||
party would have to <emphasis>wait</emphasis> for the other affecting performance, the buffer alleviates this by |
||||
allowing entire blocks of data to move at once rather then in small chunks. The data is written and read only once from |
||||
the buffer. Further more, the buffers are <emphasis>visible</emphasis> to at least one party which is aware of it.</para> |
||||
<para>A cache on the other hand by definition is hidden and neither party is aware that caching occurs.It as well improves |
||||
performance but does that by allowing the same data to be read multiple times in a fast fashion.</para> |
||||
|
||||
<para>A further explanation of the differences between two can be found |
||||
<ulink url="http://en.wikipedia.org/wiki/Cache#The_difference_between_buffer_and_cache">here</ulink>.</para> |
||||
</sidebar> |
||||
|
||||
<para>At its core, the abstraction applies caching to Java methods, reducing thus the number of executions based on the |
||||
information available in the cache. That is, each time a <emphasis>targeted</emphasis> method is invoked, the abstraction |
||||
will apply a caching behaviour checking whether the method has been already executed for the given arguments. If it has, |
||||
then the cached result is returned without having to execute the actual method; if it has not, then method is executed, the |
||||
result cached and returned to the user so that, the next time the method is invoked, the cached result is returned. |
||||
This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result |
||||
reused without having to actually execute the method again. The caching logic is applied transparently without any interference |
||||
to the invoker.</para> |
||||
|
||||
<important>Obviously this approach works only for methods that are guaranteed to return the same output (result) for a given input |
||||
(or arguments) no matter how many times it is being executed.</important> |
||||
|
||||
<para>To use the cache abstraction, the developer needs to take care of two aspects: |
||||
<itemizedlist> |
||||
<listitem>caching declaration - identify the methods that need to be cached and their policy</listitem> |
||||
<listitem>cache configuration - the backing cache where the data is stored and read from</listitem> |
||||
</itemizedlist> |
||||
</para> |
||||
|
||||
<para>Note that just like other services in Spring Framework, the caching service is an abstraction (not a cache implementation) and requires |
||||
the use of an actual storage to store the cache data - that is, the abstraction frees the developer from having to write the caching |
||||
logic but does not provide the actual stores. There are two integrations available out of the box, for JDK <literal>java.util.concurrent.ConcurrentMap</literal> |
||||
and <ulink url="http://ehcache.org/">Ehcache</ulink> - see <xref linkend="cache-plug"/> for more information on plugging in other cache stores/providers.</para> |
||||
</section> |
||||
|
||||
<section id="cache-annotations"> |
||||
<title>Declarative annotation-based caching</title> |
||||
|
||||
<para>For caching declaration, the abstraction provides two Java annotations: <literal>@Cacheable</literal> and <literal>@CacheEvict</literal> which allow methods |
||||
to trigger cache population or cache eviction. Let us take a closer look at each annotation:</para> |
||||
|
||||
<section id="cache-annotations-cacheable"> |
||||
<title><literal>@Cacheable</literal> annotation</title> |
||||
|
||||
<para>As the name implies, <literal>@Cacheable</literal> is used to demarcate methods that are cacheable - that is, methods for whom the result is stored into the cache |
||||
so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method. In its simplest form, |
||||
the annotation declaration requires the name of the cache associated with the annotated method:</para> |
||||
|
||||
<programlisting language="java"><![CDATA[@Cacheable("books") |
||||
public Book findBook(ISBN isbn) {...}]]></programlisting> |
||||
|
||||
<para>In the snippet above, the method <literal>findBook</literal> is associated with the cache named <literal>books</literal>. Each time the method is called, the cache |
||||
is checked to see whether the invocation has been already executed and does not have to be repeated. While in most cases, only one cache is declared, the annotation allows multiple |
||||
names to be specified so that more then one cache are being used. In this case, each of the caches will be checked before executing the method - if at least one cache is hit, |
||||
then the associated value will be returned:</para> |
||||
<note>All the other caches that do not contain the method will be updated as well event though the cached method was not actually |
||||
executed.</note> |
||||
|
||||
<programlisting language="java"><![CDATA[@Cacheable({ "books", "isbns" }) |
||||
public Book findBook(ISBN isbn) {...}]]></programlisting> |
||||
|
||||
<section id="cache-annotations-cacheable-default-key"> |
||||
<title>Default Key Generation</title> |
||||
|
||||
<para>Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for cache access. |
||||
Out of the box, the caching abstraction uses a simple <literal>hash-code</literal> based <interfacename>KeyGenerator</interfacename> that computes the key based on the |
||||
hashes of all objects used for method invocation. This approach works well for objects with <emphasis>natural keys</emphasis> as long as |
||||
the <literal>hashCode()</literal> reflects that. If that is not the case then |
||||
for distributed or persistent environments, the strategy needs to be changed as the objects hashCode is not preserved. |
||||
In fact, depending on the JVM implementation or running conditions, the same hashCode can be reused for different objects, in the same VM instance.</para> |
||||
|
||||
<para>To provide a different <emphasis>default</emphasis> key generator, one needs to implement the <interfacename>org.springframework.cache.KeyGenerator</interfacename> interface. |
||||
Once <link linkend="cache-configuration">configured</link>, the generator will be used for each declaration that doesn not specify its own key generation strategy (see below). |
||||
</para> |
||||
</section> |
||||
|
||||
<section id="cache-annotations-cacheable-key"> |
||||
<title>Custom Key Generation Declaration</title> |
||||
|
||||
<para>Since caching is generic, it is quite likely the target methods have various signatures that cannot be simply mapped on top of the cache structure. This tends to become |
||||
obvious when the target method has multiple arguments out of which only some are suitable for caching (while the rest are used only by the method logic). For example:</para> |
||||
|
||||
<programlisting language="java"><![CDATA[@Cacheable("books") |
||||
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed]]></programlisting> |
||||
|
||||
<para>At first glance, while the two <literal>boolean</literal> arguments influence the way the book is found, they are no use for the cache. Further more what if only one of the two |
||||
is important while the other is not?</para> |
||||
|
||||
<para>For such cases, the <literal>@Cacheable</literal> annotation allows the user to specify how the key is generated through its <literal>key</literal> attribute. |
||||
The developer can use <link linkend="expressions">SpEL</link> 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 <link linkend="cache-annotations-cacheable-default-key">default</link> generator since |
||||
methods tend to be quite different in signatures as the code base grows; while the default strategy might work for some methods, it rarely does for all methods.</para> |
||||
|
||||
<para> |
||||
Below are some examples of various SpEL declarations - if you are not familiar with it, do yourself a favour and read <xref linkend="expressions"/>: |
||||
</para> |
||||
|
||||
<programlisting language="java"><!-- select 'isbn' argument --> |
||||
@Cacheable(value="book", <emphasis role="bold">key="isbn"</emphasis> |
||||
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
||||
|
||||
<!-- select nested property of a certain argument --> |
||||
@Cacheable(value="book", <emphasis role="bold">key="isbn.rawNumber"</emphasis>) |
||||
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
||||
|
||||
<!-- invoke arbitrary method using certain arguments --> |
||||
@Cacheable(value="book", <emphasis role="bold">key="T(someType).hash(isbn)"</emphasis>) |
||||
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) |
||||
</programlisting> |
||||
|
||||
<para>The snippets above, show how easy it is to select a certain argument, one of its properties or even an arbitrary (static) method.</para> |
||||
</section> |
||||
|
||||
<section id="cache-spel-contex"> |
||||
<title>Available caching <literal>SpEL</literal> evaluation context</title> |
||||
|
||||
<para>Each <literal>SpEL</literal> expression evaluates again a dedicated <literal><link linkend="expressions-language-ref">context</link></literal>. In addition |
||||
to the build in parameters, the framework provides dedicated caching related metadata such as the argument names. The next table lists the items made available to the context |
||||
so one can use them for key and conditional(see next section) computations:</para> |
||||
|
||||
<table id="cache-spel-context-tbl" pgwide="1"> |
||||
<title>Cache SpEL available metadata</title> |
||||
<tgroup cols="3"> |
||||
<colspec align="center" /> |
||||
<thead> |
||||
<row> |
||||
<entry>Name</entry> |
||||
<entry>Location</entry> |
||||
<entry>Description</entry> |
||||
<entry>Example</entry> |
||||
</row> |
||||
</thead> |
||||
<tbody> |
||||
<row> |
||||
<entry>methodName</entry> |
||||
<entry>root object</entry> |
||||
<entry>The name of the method being invoked</entry> |
||||
<entry><screen>#root.methodName</screen></entry> |
||||
</row> |
||||
<row> |
||||
<entry>caches</entry> |
||||
<entry>root object</entry> |
||||
<entry>Collection of caches against which the current method is executed</entry> |
||||
<entry><screen>#root.caches[0].name</screen></entry> |
||||
</row> |
||||
<row> |
||||
<entry><emphasis>parameter name</emphasis></entry> |
||||
<entry>evaluation context</entry> |
||||
<entry>Name of any of the method parameter. If for some reason the names are not available (ex: no debug information), |
||||
the parameter names are also available under the <literal><![CDATA[p<#arg>]]></literal> where |
||||
<emphasis><![CDATA[#arg]]></emphasis> stands for the parameter index (starting from 0).</entry> |
||||
<entry><screen>iban</screen> or <screen>p0</screen></entry> |
||||
</row> |
||||
</tbody> |
||||
</tgroup> |
||||
</table> |
||||
</section> |
||||
</section> |
||||
|
||||
<section id="cache-annotations-evict"> |
||||
|
||||
</section> |
||||
</section> |
||||
|
||||
<section id="cache-configuration"> |
||||
<title>Configuring the <interfacename>Cache</interfacename></title> |
||||
<para></para> |
||||
</section> |
||||
|
||||
<section id="cache-plug"> |
||||
<title>Plugging-in different back-end caches</title> |
||||
<para></para> |
||||
</section> |
||||
|
||||
</chapter> |
||||
Loading…
Reference in new issue