Spring @Cacheable and @CacheEvict explained in simple terms
In Spring 3.1 we have a feature called Caching, It caches the data based on java method execution, if the java method executed before with same parameters it returns the cached data, other wise it will execute the method and puts the data into cache.
To enable caching :
<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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven /> //your beans </beans>
<cache:annotation-driven /> Will recognize the spring cache annotations @Cacheable and @CacheEvict
Spring cache provides
@Cacheable annotation and @CacheEvict annotation.
Put @Cacheable on the method which you want to cache.
Ex:
@Cacheable(value="messagecache", key="#id", condition="id < 10") public String getMessage(int id){ return "hello"+id; }
Here getMessage() method is marked with @Cacheable, whenever getMessage() is called it will check the messagecache, if the data is already in messagecache it will return that otherwise it will executes the getMessage() and returns data.
@Cacheable annotations has 3 attributes
- Value : is the cache name and it is mandatory, in example it is “messagecache”
- Key: based on this data will be cached and it is optional
- Condition: based on the condition data will be cached. In example if the id < 10 then only data will be cached otherwise won’t. it is optional
@CacheEvict annotation will be used to delete the data from existing cache.
@CacheEvict(“employees”)
public void saveEmployee(Employee e){
}
Here whenever a saveEmployee() is called cache will be deleted.
@CacheEvict has 5 attributes:
- Value, 2 Key, 3 condition are similar to @Cacheable, apart from these 3 we have another 2 attributes
- allEntries : is a Boolean type and delete entire cache
- beforeInvocation: is Boolean type and will delete the cache before the method execution
Will go through one small example:
In this example I have used ehcache, refer www.ehcache.org for more information
applicationContext.xml
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Scans within the base package of the application for @Components to configure as beans --> <context:component-scan base-package="com" /> <!-- Process cache annotations --> <cache:annotation-driven /> <!-- Configuration for using Ehcache as the cache manager--> <bean id="cacheManager" p:cache-manager-ref="ehcache"/> <bean id="ehcache" p:config-location="classpath:ehcache.xml"/> <bean id="employee" class="com.java2practice.model.Employee"/> </beans>
ehcache.xml
<ehcache> <diskStore path="java.io.tmpdir"/> <cache name="employeeCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> </ehcache>
Employee pojo:
package com.java2practice.model; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.Cacheable; public class Employee { Logger logger = LoggerFactory.getLogger(getClass()); @Cacheable(value="employeeCache", key = "#id") public String getEmployee(Integer id){ logger.info("get employee called"); return "employee"+id; } }
This Employee class is injected into controller:
Controller :
package com.java2practice.web; import java.util.HashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import com.java2practice.model.Employee; @Controller public class WebController { @Autowired Employee employee; @RequestMapping("/index.htm") public String homePage(@RequestParam(required= false) Integer id, HashMap<String, String> map){ map.put("message", employee.getEmployee(id)); return "index"; } }
And finally here is my JSP code:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Cache Example</title> </head> <body> <h1>This is ${message }</h1> </body> </html>
First time for the URL : http://localhost:8080/springcache/index.htm?id=1
Employee class getEmployee() method will be executed and data will be placed in cache. second time for the same request with id=1 getEmployee() method wont be executed, this can be seen by using the log message “get employee called” prints only for the first time.
And for the http://localhost:8080/springcache/index.htm?id=2
getEmployee() will be called and data will be placed in cache.
Here id is the Key, if id changes method will be executed and data will be placed in cache with the key.
When ever the requests comes for the @Cacheable annotated method, spring will check the key in corresponding cache if the cache has key in it. data will be returned from the cache other wise method will be executed.
Instead of ehcache we can use Spring SimpleCacheManager also:
<!-- Configuration for using SimpleCacheManager as the cache manager--> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager" > <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="employeeCache"/> </set> </property> </bean>
Nice and simple explanation !!
Thank you very much sir….!!!
Great explanation and very easy to follow. Thank you very much for this post!!
can you please post the source code.
This @cacheEvict is to delete cache objects from 2nd level is it ? Please clarify. thx
How we can conform that the cache has been cleared?
Very good explanation. thank u.
How can we evict the cache while a session expired or while log out hence we can get a new cache on next login