{"id":1188,"date":"2012-07-01T18:30:49","date_gmt":"2012-07-01T16:30:49","guid":{"rendered":"http:\/\/doanduyhai.wordpress.com\/?p=1188"},"modified":"2012-07-01T18:30:49","modified_gmt":"2012-07-01T16:30:49","slug":"cache-abstraction-in-spring-3","status":"publish","type":"post","link":"https:\/\/www.doanduyhai.com\/blog\/?p=1188","title":{"rendered":"Cache abstraction in Spring 3"},"content":{"rendered":"<p>In this article we&#8217;ll have a look at the new Spring 3 feature: caching by annotation.<\/p>\n<p><!--more--><\/p>\n<h1>I Cache abstraction layer<\/h1>\n<p>Caching is an important feature for all applications needing high performance. Many open-source frameworks are available: <strong>EhCache<\/strong>, <strong>JBoss Cache<\/strong>, <strong>OSCache<\/strong>, <strong>Open Terracota<\/strong> &#8230;<\/p>\n<p> Their integration with the application is sometimes easy sometimes more complex, depending on the desired features and use (distributed cache ? cache JMX management &#8230;)<\/p>\n<p> Spring 3 introduces a new abstraction layer for caching services. The idea is to provide a set of common features, mainly annotations, to activate and manage the caches.<\/p>\n<p> Since it is only an abstract layer, Spring 3 caching still need a concrete implementation to work. The entry point for cache implementation is the <strong>CacheManage<\/strong>r interface. By default 2 concrete implementation of <strong>CacheManager<\/strong> are provided: <\/p>\n<ul>\n<li><strong>EhCacheCacheManager<\/strong>: default implementation for EhCache<\/li>\n<li><strong>ConcurrentMapCacheManager<\/strong>: default implementation using Java <strong>ConcurrentHashMap<\/strong> as cache store<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h1>II Configuration<\/h1>\n<p>For this example we&#8217;ll use EhCache as cache implementation.<\/p>\n<p>First you need to import the Spring cache namespace and add the &amp;th;cache:annotation-driver&gt; tag.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&amp;lt;cache:annotation-driven cache-manager=&amp;quot;cacheManager&amp;quot; mode=&amp;quot;proxy&amp;quot; order=&amp;quot;1&amp;quot; \/&amp;gt;\n<\/pre>\n<ul>\n<li><strong>cache-manager<\/strong>: id or name of the bean implementing the <strong>CacheManager<\/strong> interface. Can be omitted because by convention Spring will look for any bean with the name &#8220;<strong>cacheManager<\/strong>&#8220;<\/li>\n<li><strong>mode<\/strong>: <strong>proxy<\/strong> or <strong>aspectj<\/strong>. &#8220;<strong>proxy<\/strong>&#8221; will use Spring AOP framework to proxy any class having a caching annotation (see below). &#8220;<strong>aspectj<\/strong>&#8221; will rely on AspectJ aspect for cache management<\/li>\n<li><strong>order<\/strong>: optional. If you have more than one aspect declared on the same method, it can be useful to specify the order in which they execute<\/li>\n<\/ul>\n<p>Then you need to declare the cacheManager bean and implementation instance (or factory bean)<\/p>\n<pre class=\"brush: xml; highlight: [2,7]; title: ; notranslate\" title=\"\">\n&amp;lt;bean id=&amp;quot;cacheManager&amp;quot; class=&amp;quot;org.springframework.cache.ehcache.EhCacheCacheManager&amp;quot;&amp;gt;\n\t&amp;lt;property name=&amp;quot;cacheManager&amp;quot; ref=&amp;quot;ehcache&amp;quot;\/&amp;gt;\n&amp;lt;\/bean&amp;gt;\n\n&amp;lt;!-- Ehcache library setup --&amp;gt;\n&amp;lt;bean id=&amp;quot;ehcache&amp;quot; class=&amp;quot;org.springframework.cache.ehcache.EhCacheManagerFactoryBean&amp;quot;&amp;gt;\n\t&amp;lt;property name=&amp;quot;configLocation&amp;quot; value=&amp;quot;classpath:ehcache.xml&amp;quot;\/&amp;gt;\n&amp;lt;\/bean&amp;gt;\n<\/pre>\n<p>First we declare the <strong>EhCacheCacheManager<\/strong> as default cache manager. Then we inject the <strong>EhCacheManagerFactoryBean<\/strong> so it can retrieve an instance of EhCache class.<\/p>\n<p>Please notice the very confusing naming. The <strong>EhCacheCacheManager<\/strong> instance is given the id &#8220;<strong>cacheManager<\/strong>&#8221; as per Spring convention but it also has a <strong>cacheManager property<\/strong>. This property is indeed an instance of <strong>net.sf.ehcache.CacheManager<\/strong>, different from the <strong>org.springframework.cache.CacheManager<\/strong> interface.<\/p>\n<p>Last but not least, you can specify the config file for ehcache with the property <strong>configLocation<\/strong> of <strong>EhCacheManagerFactoryBean<\/strong>. If not declared it will default to &#8220;<strong>ehcache.xml<\/strong>&#8221;<\/p>\n<p>&nbsp;<\/p>\n<h1>III Cache annotations<\/h1>\n<p>By default Spring proposes several annotations to manage your caches:<\/p>\n<ul>\n<li><strong>@Cacheable<\/strong>: put the method returned value(s) into the cache<\/li>\n<li><strong>@CacheEvict<\/strong>: remove an entry from the cache<\/li>\n<li><strong>@CachePut<\/strong>: force a cache entry to be updated<\/li>\n<\/ul>\n<p> And that&#8217;s pretty much. But don&#8217;t be fooled by their apparent simplicity, they offer a lot of possibilities to fine tune your caching.<\/p>\n<h3>A @Cacheable<\/h3>\n<p> When annotating a method with <strong>@Cacheable<\/strong>, <strong>its returned value<\/strong> will be put into the cache provided it meets some condition (if any). Consequently it does not make sense to annotate a <strong>void<\/strong> method.<\/p>\n<p> <strong>So what can be put in cache ?<\/strong> Pretty much anything, an Object, a collection (List, Map, Set, Array)&#8230;<\/p>\n<p> <strong>When is the cache activated ?<\/strong> All subsequent calls on the same method with the same arguments or cache key (we&#8217;ll see it later) will trigger the cache. Instead of executing the method, the cache is scanned to check whether a matching entry can be found, if yes then it is returned. Otherwise the method is really executed and its result put into the cache.<\/p>\n<p> Below is the pseudo-code:<\/p>\n<ul>\n<li>Method is called with arguments <strong>args<\/strong><\/li>\n<li>Use the <strong>args hashCode<\/strong> or extract the cache key from the <strong>args<\/strong> to look for an entry in the cache<\/li>\n<li>If a corresponding entry is found\n<ul>\n<li>Return the cached entry<\/li>\n<\/ul>\n<\/li>\n<li>Else\n<ul>\n<li>Execute really the method<\/li>\n<li>Put the method returned value into the cache using the <strong>args hashCode<\/strong> of extracted cache key\n\t\t<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>So what is the cache key and why do we need to use <strong>args hashCode<\/strong> ? We&#8217;ll see it shortly. First the <strong>@Cacheable<\/strong> annotation offers the following attributes:<\/p>\n<ul>\n<li><strong>value<\/strong>: mandatory, the name of the cache to work on<\/li>\n<li><strong>key<\/strong>: optional, the key used to store and fetch data from the cache<\/li>\n<li><strong>condition<\/strong>: optional, specifies the condition to verify before caching an item<\/li>\n<\/ul>\n<p>Example:<\/p>\n<pre class=\"brush: java; highlight: [1]; title: ; notranslate\" title=\"\">\n@Cacheable(value = &amp;quot;user-cache&amp;quot;, key=&amp;quot;#userSearchVO.name&amp;quot;, condition=&amp;quot;#supportUser == false&amp;quot;)\npublic User findUser(UserSearchVO userSearchVO, boolean supportUser)\n{\n\tUser foundUser = null;\n\t...\n\t...\n\t...\n\treturn foundUser;\n}\n<\/pre>\n<p> In the above example<\/p>\n<ul>\n<li><strong>value = &#8220;user-cache&#8221;<\/strong> indicates the name of the cache in which entries are stored<\/li>\n<li><strong>key=&#8221;#searchVO.name&#8221;<\/strong> defines the key to lookup into the cache. Here it is the <em>name<\/em> property of the <strong>UserSearchVO<\/strong> object<\/li>\n<li><strong>condition=&#8221;#supportUser == false&#8221;<\/strong> provides the condition to trigger the cache. The cache lookup and the method result caching are triggered only when the <em>supportUse<\/em>r flag is set to false<\/li>\n<\/ul>\n<p> Please notice that for the key and condition attributes, we are using <a href=\"http:\/\/static.springsource.org\/spring\/docs\/3.0.5.RELEASE\/reference\/expressions.html\" title=\"Spring EL\" target=\"_blank\">SpEL<\/a> to process the method arguments.<\/p>\n<p> This is very convenient because the <strong>SpEL<\/strong> expression language is extremely powerfull. For example, if the <strong>UserSearchVO<\/strong> has an attribute <em>searchType<\/em> of enum type, we can do the following:<\/p>\n<pre class=\"brush: java; highlight: [1,13]; title: ; notranslate\" title=\"\">\n@Cacheable(value = &amp;quot;user-cache&amp;quot;, key=&amp;quot;#userSearchVO.name&amp;quot;, condition=&amp;quot;#userSearchVO.searchType == ${T(com.doan.spring.cache.SearchType).CLASSIC&amp;quot;)\npublic User findUser(UserSearchVO userSearchVO, boolean supportUser)\n{\n\tUser foundUser = null;\n\t...\n\t...\n\t...\n\treturn foundUser;\n}\n\npublic enum SearchType\n{\n\tCLASSIC,\n\tADVANCED,\n\tSUPPORT\n}\n<\/pre>\n<p> In this case the cache is triggered only when the search is of type CLASSIC. <\/p>\n<blockquote><p>Note that we use <strong>==<\/strong> instead of <em>equals()<\/em> to check for the <em>searchType<\/em>. Since the attribute <em>searchType<\/em> is an <strong>enum<\/strong>, we want to check <strong>object identity (same reference)<\/strong> with == rather than <strong>object equality (same value)<\/strong> with <em>equals()<\/em>.\n<\/p><\/blockquote>\n<p>Another simple example:<\/p>\n<pre class=\"brush: java; highlight: [1,13]; title: ; notranslate\" title=\"\">\n@Cacheable(value = {&amp;quot;user-cache1&amp;quot;,&amp;quot;user-cache2&amp;quot;})\npublic User findUserByLoginAndName(String login, String name)\n{\n\tUser foundUser = null;\n\t...\n\t...\n\t...\n\treturn foundUser;\n}\n<\/pre>\n<p> In this example, we declare more than one cache (<strong>&#8220;user-cache1&#8221;<\/strong>,<strong>&#8220;user-cache2&#8221;<\/strong>) and no <strong>key<\/strong> information. <\/p>\n<p>By default if no cache key is provided Spring will compute a hash code of all method arguments (here it is <em>login<\/em> &amp; <em>name<\/em>) and use it as a key for cache lookup. <strong>If your method arguments are not of primitive type and you want them to be used as cache key, you should redefine properly the hashCode() method<\/strong>.<\/p>\n<p>Last but not least, Spring will scan each cache for key lookup, if an entry is found in any declared cache, it will be returned, <strong>subsequent caches are skipped<\/strong>. If all caches are scanned and no entry is found, the method will be executed and the result added to all the declared caches.<\/p>\n<p>&nbsp;<\/p>\n<h3>B @CacheEvict<\/h3>\n<p> The <strong>@CacheEvict<\/strong> is used to trigger <strong>explicit cache eviction<\/strong>. By default most of caching frameworks expire the cache data after some defined duration. The <strong>@CacheEvict<\/strong> annotation is usefull when you want to control explicit cache eviction upon some method calls.<\/p>\n<pre class=\"brush: java; highlight: [1]; title: ; notranslate\" title=\"\"> \n@CacheEvict(value = &amp;quot;user-cache&amp;quot;, key = &amp;quot;#user.login&amp;quot;)\npublic void updateUser(User user)\n{\n\t...\n\t...\n}\n<\/pre>\n<p> The above code is quite self-explanatory. The <strong>@CacheEvict<\/strong> annotation exposes the following attributes:<\/p>\n<ul>\n<li><strong>value<\/strong>: mandatory, the name of the cache to evict entry<\/li>\n<li><strong>key<\/strong>: optional, the key used to lookup data from the cache<\/li>\n<li><strong>condition<\/strong>: optional, specifies the condition to verify before evicting a cache entry<\/li>\n<li><strong>allEntries<\/strong>: optional, indicates that all entries from the cache should be removed<\/li>\n<li><strong>beforeInvocation<\/strong>: optional, indicates whether the cache evict should be done before or after method call<\/li>\n<\/ul>\n<p>Obviously the <strong>key<\/strong> and <strong>allEntries<\/strong> attributes are mutually exclusive.<\/p>\n<p>&nbsp;<\/p>\n<h3>C @CachePut<\/h3>\n<p> The <strong>@CachePut<\/strong> annotation allows you to &#8220;update&#8221; a cache entry. This is very similar to <strong>@Cacheable<\/strong> but for entry update. The <strong>@CachePut<\/strong> annotation has exactly the same attributes than <strong>@Cacheable<\/strong>.<\/p>\n<pre class=\"brush: java; highlight: [1]; title: ; notranslate\" title=\"\"> \n@CachePut(value = &amp;quot;user-cache&amp;quot;, key = &amp;quot;#user.login&amp;quot;)\npublic User updateUserName(User user,String newName)\n{\n\t...\n\t...\n\tuser.setName(newName);\n\t...\n\t...\n\treturn user;\n}\n<\/pre>\n<p> In the case of <strong>@CachePut<\/strong>, <strong>the method is always executed<\/strong> and its returned result is put into the cache, using the provided key (or arguments hash), replacing the old entry if necessary.<\/p>\n<p> The only case where the method is not executed is when you provide an optional <strong>@CachePut<\/strong> <strong>condition<\/strong> and the condition is not met.<br \/>\n&nbsp;<\/p>\n<h1>IV Multiple caching policies<\/h1>\n<p> Let&#8217;s suppose than we have an application with 2 cache regions: &#8220;user-cache&#8221; and &#8220;user-details-cache&#8221;.<\/p>\n<pre class=\"brush: java; highlight: [1]; title: ; notranslate\" title=\"\">\n@Cacheable(value = &amp;quot;user-cache&amp;quot;, key=&amp;quot;#login&amp;quot;)\npublic User findUserByLogin(String login)\n{\n\t...\n\t...\n\treturn foundUser;\n}\n...\n...\n\n@Cacheable(value = &amp;quot;user-details-cache&amp;quot;, key=&amp;quot;#login&amp;quot;)\npublic UserDetails findUserDetailsByLogin(String login)\n{\n\t...\n\t...\n\treturn userDetails;\n}\n<\/pre>\n<p>It is possible to trigger the eviction from both caches with the same key:<\/p>\n<pre class=\"brush: java; highlight: [3,4]; title: ; notranslate\" title=\"\">\n@CacheEvict(value = \n{\n\t&amp;quot;user-cache&amp;quot;,\n\t&amp;quot;user-details-cache&amp;quot;\n}, key=&amp;quot;#login&amp;quot;)\npublic void updateUserDetails(String login, UserDetails newUserDetails)\n{\n\t...\n\t...\n}\n<\/pre>\n<p> In this particular example, it&#8217;s working very well because we access both caches with the same key. What if we want to evict from those caches with different keys ?<\/p>\n<pre class=\"brush: java; highlight: [1,2]; title: ; notranslate\" title=\"\">\n@CacheEvict(value = &amp;quot;user-cache&amp;quot;, key=&amp;quot;#login&amp;quot;)\n@CacheEvict(value = &amp;quot;user-details-cache&amp;quot;, key=&amp;quot;#newUserDetails.id&amp;quot;)\npublic void updateUserDetails(String login, UserDetails newUserDetails)\n{\n\t...\n\t...\n}\n<\/pre>\n<p> It&#8217;s simply not possible because Java does not allow you to have more than one type of annotation on the same method.<\/p>\n<p> For this kind of use case, Spring provides a generic <strong>@Caching<\/strong> annotation. This annotation simply allows you to group caching annotations of same type like <strong>@Cacheable<\/strong>, <strong>@CacheEvict<\/strong> or <strong>@CachePut<\/strong>.<\/p>\n<p> With this said, our example becomes:<\/p>\n<pre class=\"brush: java; highlight: [1,2,3,4]; title: ; notranslate\" title=\"\">\n@Caching(evict = {\n\t@CacheEvict(value = &amp;quot;user-cache&amp;quot;, key=&amp;quot;#login&amp;quot;),\n\t@CacheEvict(value = &amp;quot;user-details-cache&amp;quot;, key=&amp;quot;#newUserDetails.id&amp;quot;)\n})\npublic void updateUserDetails(String login, UserDetails newUserDetails)\n{\n\t...\n\t...\n}\n<\/pre>\n<p> And you&#8217;re done!<\/p>\n<p> Of course if you try to mix caching annotation of different type in the same <strong>@Caching<\/strong>, needless to say that you are running into big trouble&#8230;<\/p>\n<p>&nbsp;<\/p>\n<h1>V Object mutability gotcha<\/h1>\n<p> So far the caching abstraction infrastructure proposed by Spring is very convenient. However, for having used it in a real project, I&#8217;ve spotted 1 pain point : object mutability issues, thought this point is not Spring&#8217;s specific but common to all caching frameworks.<\/p>\n<p> Indeed when you get an instance of User after calling <em>findUserByLogin(String login)<\/em>, this instance may come from the cache directly.<\/p>\n<p> If you are modifying this <strong>User<\/strong> instance (changing an user property for example), you are indeed modifying the object which is in the cache directly !!!<\/p>\n<p> The immediate consequence is that on the same server, if another client looks for the same user, the cache will give him the same <strong>User<\/strong> instance that has been modified earlier&#8230;<\/p>\n<p> There are 2 solutions for this issue. <\/p>\n<ol>\n<li>The first idea is to use objects returned from cache as <strong>read-only<\/strong> but this rule cannot be enforced easily. Suppose that you put <strong>@Cacheable<\/strong> annotation on a Repository or DAO method, the developper that calls this method from Service layer may not be aware that he&#8217;s getting a cached instance and may modify it.<\/li>\n<li>The second fix is to perform a deep copy of the returned object. But again, implementing deep copy is not an easy task (as I mentioned in my article about <a href=\"http:\/\/doanduyhai.wordpress.com\/2012\/05\/26\/object-immutability-in-java\/\" title=\"Object Immutability\" target=\"_blank\">Object Immutablity<\/a>) and the deep copy should be done by the <strong>caller<\/strong> (Service layer in our example). The same issue with &#8220;<em>knowing that we&#8217;re dealing with cached instances<\/em>&#8221; mentioned above also applies.<\/li>\n<\/ol>\n<p>Fortunately, latest <strong>EHCache<\/strong> versions provide a very usefull feature: <strong>copyOnRead<\/strong>.<\/p>\n<pre class=\"brush: xml; highlight: [7]; title: ; notranslate\" title=\"\">\n&amp;lt;cache name=&amp;quot;user-cache&amp;quot;\n\t...\n\teternal=&amp;quot;false&amp;quot;\n\ttimeToIdleSeconds=&amp;quot;10&amp;quot;\n\ttimeToLiveSeconds=&amp;quot;1800&amp;quot;\n\toverflowToDisk=&amp;quot;false&amp;quot;\n\tcopyOnRead=&amp;quot;true&amp;quot;\n&amp;gt;\n&amp;lt;\/cache&amp;gt;\n<\/pre>\n<p> With this flag turned on, <strong>EHCache<\/strong> will return you a <strong>deep copy<\/strong> of the object instance that is in the cache, not the instance itself. So mutability issue is solved.<\/p>\n<p> Of course there is no free lunch, this convenient feature comes with its counterparts, <strong>the cost of serialization\/deserialization<\/strong> because the default implementation is plain Java object serialization.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article we&#8217;ll have a look at the new Spring 3 feature: caching by annotation.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[21,14],"tags":[],"_links":{"self":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1188"}],"collection":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1188"}],"version-history":[{"count":0,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1188\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.doanduyhai.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}