Cache Abstraction In Spring (Example for @Cacheable and @CacheEvict)

Since Spring 3 a new abstraction layer for caching services has been introduced called Cache Abstraction. The idea is to provide a set of common features, mainly annotations, to activate and manage the caches. Cache Abstraction is nothing but applying caching to Java methods, reducing thus the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction will apply a caching behavior checking whether the method has been already executed for the given arguments.

  • If yes, then the cached result is returned without having to execute the actual method.
  • If no, 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.

As the name implies, Cache Abstraction is not an actual implementation. Hence it requires the use of an actual storage to store the cache data. Ehcache support is provided out of the box. There is also an implementation based on JDK’s ConcurrentMap and you can actually plug-in different back-end caches.

follow us on @twitter and @facebook

Annotation based Caching

For caching declaration, the abstraction provides following Java annotations:

  • @Cacheable: put the method returned value(s) into the cache.
  • @CacheEvict: remove an entry from the cache.
  • @CachePut: force a cache entry to be updated.
  • @Caching: @Caching allows multiple nested @Cacheable, @CachePut and @CacheEvict to be used on the same method.

Enable caching annotations

Caching feature needs to be declaratively enabled by using either of following ways:

  • Add the annotation @EnableCaching to one of your @Configuration classes.
  • Alternatively for XML configuration use the cache:annotation-driven element.

@Cacheable annotation

In Spring @Cacheable is useful in the case when a very complex code is running for many times. @Cacheable caches the result per input values of methods, so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method. Spring handles this functionality by second level cache. @Cacheable is used in the code as shown below:

@Cacheable("product")
public String getProduct(int productId){}

@CacheEvict annotation

The cache abstraction allows not just population of a cache store but also eviction i.e removes data from the cache. @CacheEvict in spring is used to evict cache value. @CacheEvict is applied on the method. This annotation has an extra parameter allEntries which indicates whether a cache-wide eviction needs to be performed rather then just an entry one (based on the key):

@CacheEvict(value = "product", allEntries=true)
public void setProduct(int productId){}

This option comes in handy when an entire cache region needs to be cleared out – rather then evicting each entry (which would take a long time since it is inefficient), all the entires are removed in one operation as shown above.

Example for @Cacheable and @CacheEvict

The following example demonstrates the use of the @Cacheable and @CacheEvict annotations and enabling them in the Spring application. Let us have working Eclipse IDE in place and follow the following steps to create a Spring application:

  1. Create a project: Create a project with a name SpringCacheAbstraction and create a package com.javabeat under the src directory in the created project.
  2. Add Libraries: Add required Spring libraries using Add External JARs option as explained in the article Customizing callback methods. Along with te specified JARs in the link, also add make sure these JARs are in the buildpath of the project:aopalliance-x.0.jar, ehcache-x.x.0.jar(you can download latest ehcache.jar from here: )
  3. Create source files: Create Java classes Product,and MainApp under the com.javabeat package.
  4. Create configuration file: Create XML based configuration file Beans.xml and ehcache.xml under src directory.

Contents of Product.java are as below:

package com.javabeat;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

public class Product {
	@CacheEvict(value = "prod", allEntries = true)
	public void setProduct(int productId) {
		System.out.println("execute setProduct method..");
	}

	@Cacheable("prod")
	public String getProduct(int productId) {
		System.out.println("execute getProduct method..");
		if (productId == 1) {
			return "Product A";
		} else {
			return "Product B";
		}
	}
}

Contents of MainApp.java are as below:

package com.javabeat;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
	public static void main(String... args) {
		AbstractApplicationContext context = new ClassPathXmlApplicationContext(
				"Beans.xml");
		Product product = (Product) context.getBean("product");

		// calling getProduct method first time.
		System.out.println(product.getProduct(1));

		// calling getProduct method second time. This time, method will not
		// execute.
		System.out.println(product.getProduct(1));

		// calling setProduct method to evict the cache value
		product.setProduct(1);

		// calling getProduct method third time. This time, method will execute
		// again.
		System.out.println(product.getProduct(1));
	}
}

There is a method getProduct in the class Product. We have called it three times. First time getProduct methods runs and sets result in cache named prod. Second time getProduct does not run because there is value in cache. Now call setProduct to remove the cache value. Again run getProduct method . This method will run because cache value is empty because of eviction.

Contents of Beans.xml are as below:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-3.0.xsd


http://www.springframework.org/schema/cache


http://www.springframework.org/schema/cache/spring-cache.xsd">

<cache:annotation-driven />

	<bean id="product" class="com.javabeat.Product">
	</bean>
	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
	<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml"/>

</beans>

To get @Cacheable our result we have to configure org.springframework.cache.ehcache.EhCacheCacheManager in Beans xml as seen above. All second level configuration properties are configured in an ehcache.xml and that xml file is configured in Beans.xml as seen above.

Contents of ehcache.xml are as below:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
	<defaultCache eternal="true" maxElementsInMemory="100"
		overflowToDisk="false" />
	<cache name="prod" maxElementsInMemory="10000" eternal="true"
		overflowToDisk="false" />
</ehcache>

Cache name has been declared as which is used in the Product.java class for caching result of the method.

Execute

Once all the code is ready, execute the MainApp class and if everythingis fine, the following output should appear on the console:

execute getProduct method..
Product A
Product A
execute setProduct method..
execute getProduct method..
Product A

Summary

In this post we saw the basics of Cache Abstraction in Spring. An example is used to demostrate the use of @Cacheable and @CacheEvict. In the next post I shall cover some other feature of the Spring. If you are interested in receiving the future articles, please subscribe here.  follow us on @twitter and @facebook

Comments

comments

About Manisha Patil

Manisha S Patil, currently residing at Pune India. She is currently working as freelance writer for websites. She had earlier worked at Caritor Bangalore, TCS Bangalore and Sungard Pune. She has 5 years of experience in Java/J2EE technologies.

Speak Your Mind

*