Evaluate after the method has been called
Hooray, as of Spring 3.2 the framework allows for this using Spring SPEL and unless
. Note from the java doc surrounding Cacheable:
public abstract String unless
Spring Expression Language (SpEL) attribute used to veto method caching.
Unlike condition(), this expression is evaluated after the method has been called and can therefore refer to the result. Default is "", meaning that caching is never vetoed.
The important aspect is that unless
is evaluated after the method has been called. This makes perfect sense because the method will never get executed if the key is already in the cache.
So in the above example you would simply annotate as follows (#result is available to test the return value of a method):
@Cacheable(value="defaultCache", key="#pk", unless="#result == null")
public Person findPerson(int pk) {
return getSession.getPerson(pk);
}
I would imagine this condition arises from the use of pluggable cache implementations such as Ehcache which allows caching of nulls. Depending on your use case scenario this may or may not be desirable.
Using Spring and Ehcache
Introduction
Ehcache has had excellent Spring integration for many years. This page demonstrates two new ways of using Ehcache with Spring.
Spring 3.1
Spring Framework 3.1 added a new generic cache abstraction for transparently applying caching to Spring applications. It adds caching support for classes and methods using two annotations:
@Cacheable
Cache a method call. In the following example, the value is the return type, a Manual. The key is extracted from the ISBN argument using the id.
@Cacheable(value="manual", key="#isbn.id") public Manual findManual(ISBN isbn, boolean checkWarehouse)
@CacheEvict
Clears the cache when called.
@CacheEvict(value = "manuals", allEntries=true) public void loadManuals(InputStream batch)
Spring 3.1 includes an Ehcache implementation. See the Spring 3.1 JavaDoc.
It also does much more with SpEL expressions. See http://blog.springsource.com/2011/02/23/spring-3-1-m1-caching/ for an excellent blog post covering this material in more detail.
Spring 2.5 - 3.1: Ehcache Annotations For Spring
This open source, led by Eric Dalquist, predates the Spring 3.1 project. You can use it with earlier versions of Spring or you can use it with 3.1.
@Cacheable
As with Spring 3.1 it uses an @Cacheable annotation to cache a method. In this example calls to findMessage are stored in a cache named "messageCache". The values are of type Message
. The id for each entry is the id
argument given.
@Cacheable(cacheName = "messageCache") public Message findMessage(long id)
@TriggersRemove
And for cache invalidation, there is the @TriggersRemove annotation. In this example, cache.removeAll()
is called after the method is invoked.
@TriggersRemove(cacheName = "messagesCache", when = When.AFTER_METHOD_INVOCATION, removeAll = true) public void addMessage(Message message)
See http://blog.goyello.com/2010/07/29/quick-start-with-ehcache-annotations-for-spring/ for a blog post explaining its use and providing further links.
출처 - http://ehcache.org/documentation/2.5/integrations/spring
SPRING 3.1 M1: CACHE ABSTRACTION |
One of the major features added in Spring Framework 3.1 M1 is the generic cache abstraction for transparently applying caching to Spring applications. Just like the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.
Purpose
Caches are in general used to improve application performance by transparently serving frequently accessed data in a faster fashion such as serving data from local memory rather than from the network. Many of you have already used caching, whether knowingly or not: most ORM/JPA frameworks provide dedicated caching functionality (also known as the 2nd-level cache). Spring 3.1 M1 however introduces a generic cache mechanism that can be applied to any Java class, method or library: one can use it in conjunction with an existing caching infrastructure, to add caching to APIs without such support (for example JDBC) or simply to improve the performance of a slow, time-consuming and resource-hungry method.
Meet @Cacheable
, @CacheEvict
and SpEL
Let us see what it takes to cache an arbitrary method:
By marking the method with the @Cacheable
annotation, we tell the container that thefindBook
method is backed by the cache entry books
. That is each time the method is called, a cache lookup is performed using as key the method parameters (in this case theisbn
argument). If a value is found, it will be returned and the method execution skipped. However, if the key is not found, the method is executed as usual and its result stored in the cache so the next time the method is invoked, the result can be returned without actually executing the (expensive or slow) method.
In practice not all methods have only one argument or, worse yet, the parameters are not suitable as cache keys – take for example a variation of the method above:
In such cases, one can use Spring 3 Spring Expression Language or SpEL to cherry pick the proper arguments, navigate the object tree
or compute the key on the fly, even call arbitrary methods without having to write any code:
Additionally, one can specify when or if the caching should occur: whether the cache should be inspected or completely disregarded and the method executed normally. It's up to the developer to decide what the criteria is: can be anything from the key size or type to the time of day or result of arbitrary methods: SpEL supports it all:
The cache abstraction also supports eviction of cache entries or of an entire cache through the @CacheEvict
annotation. To evict a cache once it becomes invalid (for example because the cached data has been updated) one can use the following:
Once the annotations are in place, one can simply "enable" the caching functionality with one line (or three if you count the schema declaration):
Just like the rest of the annotation-driven
element, the cache one uses defaults in its simplest form but can be used to pick between proxy and byte-code weaving of cached classes or to wire in the desired cache implementation.
Declaring a cache implementation
So far, we discussed the declarative aspect of the caching abstraction: how to add and remove data from the cache based on your POJOs. But what are the backing cache implementations that one can use?
Out of the box, Spring provides integration with ehcache and JDK ConcurrentHashMap
great for small, non-distributed environments or testing:
What about [xx] library – when will it be supported ?
For the moment we do not plan to support other caching libraries inside Spring Framework simply because of the sheer number of options out there, the dependency impact (many are bigger in size than the cache abstraction), and the maintenance and licensing issues. To plug-in a custom cache provider, we encourage developers to look at the caching SPIpackage and its two interfaces: CacheManager and Cache. Besides the implementations available out of the box, one can look at the GemFire implementation, scheduled for the next major release version of Spring GemFire.
How does the caching abstraction compare to other caches (e.g. JPA 2nd-level cache) ?
In general, the two caching mechanisms can happily coexist as long as the developer pays attention to any domain overlap. Taking the example of the JPA 2nd-level cache, one can used it for data access through JPA while using Spring caching for the web-tier or remote service calls. One can go a step further by reusing the backed cache between the two mechanisms if that applies.
Summary
I hope you enjoyed this quick introductory entry to the new caching feature in Spring 3.1. For more information, please see the relevant reference documentation chapter and the SPIjavadoc. And do let us know what you think – we are interested in your feedback! You can reach us through the forum, blog post comments, our issue tracker or yours truly on Twitter.
SIMILAR POSTS
출처 - http://blog.springsource.com/2011/02/23/spring-3-1-m1-caching/
Quick start with method caching using Spring 3.1 and Ehcache
A while ago I wrote about method caching in Java using Ehcache Annotations for Spring. However, Spring 3.1 release, among many enhancements, brings native support for method caching with so-called cache abstraction. The main usage scenario of cache abstraction is to reduce the number of expensive executions and/or slow methods returning the same result for given parameters. The usage of Spring cache abstraction consists of two aspects: cache declaration and configuration. Spring provides many ways for declaring and configuring caching in an application: annotation-based, XML-based or mixed. In this article I will create an application that utilizes cache using Java-based approach. The example used in this article is a bit modified project taken from the original article.
Create a new web project
To create a project we usespring-mvc-quickstart
Maven2 archetype that can be found here:spring-mvc-quickstart-archetype. After installing the archetype in your local repository we run the following command to create new Spring 3.1 MVC project:mvn archetype:generate -DarchetypeGroupId=com.github.spring-mvc-archetypes -DarchetypeArtifactId=spring-mvc-quickstart -DarchetypeVersion=1.0.0-SNAPSHOT -DgroupId=my.groupid -DartifactId=my-artifactId -Dversion=versionOnce the project is created it may be imported to Eclipse/STS using “Import Existing Maven Projects”.
Ehcache dependency
Spring cache abstraction currently supports two cache storages: JDK ConcurrentMap-based Cache and Ehcache-based Cache that will be used in this example. In order to use Ehcache-based Cache storage in our application we need to add appropriate dependency on Ehcache in POM file:1 2 3 4 5 | < dependency > < groupId >net.sf.ehcache</ groupId > < artifactId >ehcache-core</ artifactId > < version >2.5.0</ version > </ dependency > |
Cache configuration
Our application is configured via Spring@Configuration
classes and the cache configuration will be configured the same way thanks to @EnableCaching
annotation that enables Spring annotation-driven cache management capability to the application.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | @Configuration @EnableCaching public class CachingConfig { @Bean public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() { EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean(); ehCacheManagerFactoryBean.setConfigLocation( new ClassPathResource( "ehcache.xml" )); return ehCacheManagerFactoryBean; } @Bean public CacheManager cacheManager() { EhCacheCacheManager cacheManager = new EhCacheCacheManager(); cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject()); return cacheManager; } } |
src/main/resources
:1 2 3 4 5 | <? xml version = "1.0" encoding = "UTF-8" ?> < ehcache > < defaultCache eternal = "true" maxElementsInMemory = "100" overflowToDisk = "false" /> < cache name = "messageCache" maxElementsInMemory = "10" eternal = "true" overflowToDisk = "false" /> </ ehcache > |
CachingConfig
class may be now imported by our WebMvcConfig class:1 2 3 4 5 6 7 | @Configuration @EnableWebMvc @ComponentScan (basePackages = { "com.github.springmvcdemos.cachedemo" }, excludeFilters = @Filter (type = FilterType.ANNOTATION, value = Configuration. class )) @Import ({PersistanceConfig. class , CachingConfig. class }) public class WebMvcConfig extends WebMvcConfigurerAdapter { // ... } |
Cache declaration
Now it’s time to add caching capabilities to the application. We declare caching onMessageRepository
by setting the following rules:- use cache with name
messageCache
for caching messages for invocations offindByRecipient(String)
- use cache with name
messagesCache
for caching messages for invocations offindAll()
- remove all elements from both caches after the invocation of createMessage(Message)
org.springframework.cache.annotation.Cacheable
annotation – method or type level annotation indicating the method or multiple methods can be cached. To invalidate the cache we useorg.springframework.cache.annotation.CacheEvict
annotation:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Repository public class MessageRepository { @PersistenceContext private EntityManager entityManager; @CacheEvict (value = { "messageCache" , "messagesCache" }, allEntries = true , beforeInvocation = false ) public Message createMessage(Message message) { entityManager.persist(message); return message; } @Cacheable (value = "messagesCache" ) public List findAll() { return entityManager.createQuery( "select m from Message m" , Message. class ).getResultList(); } @Cacheable (value = "messageCache" ) public List findByRecipient(String recipient) { return entityManager.createQuery( "select m from Message m where m.recipient = :recipient" , Message. class ).setParameter( "recipient" , recipient).getResultList(); } public Message findById(Long id) { return entityManager.find(Message. class , id); } } |
ehcache.xml
file shown in previous section. To quickly see the result of the method caching we may create a @Controller
that calls our repository methods:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | @Controller @Transactional (readOnly = true ) public class MessageController { private MessageRepository repo; @Autowired public MessageController(MessageRepository repo) { this .repo = repo; } public MessageController() { } @RequestMapping (method = RequestMethod.GET, value = "/" ) public ModelAndView index() { return new ModelAndView( "index" ); } @RequestMapping (method = RequestMethod.GET, value = "/message/add" ) public ModelAndView messageForm() { return new ModelAndView( "message-form" , "command" , new Message()); } @RequestMapping (method = RequestMethod.POST, value = "/message/add" ) @Transactional public ModelAndView addMessage( @ModelAttribute Message message) { message = repo.createMessage(message); return addMessageToModel(message); } @RequestMapping (method = RequestMethod.GET, value = "/message/{id}" ) public ModelAndView getMessageById( @PathVariable ( "id" ) long id) { Message message = repo.findById(id); return addMessageToModel(message); } private ModelAndView addMessageToModel(Message message) { ModelAndView mav = new ModelAndView( "message-details" ); mav.addObject( "message" , message); return mav; } @RequestMapping (method = RequestMethod.GET, value = "/message" , params= "recipient" ) public ModelAndView getMessagesByRecipients( @RequestParam String recipient) { return addMessagesToModel(repo.findByRecipient(recipient)); } @RequestMapping (method = RequestMethod.GET, value = "/message" ) public ModelAndView getAllMessages() { return addMessagesToModel(repo.findAll()); } private ModelAndView addMessagesToModel(Collection messages) { ModelAndView mav = new ModelAndView( "messages" ); mav.addObject( "messages" , messages); return mav; } } |
INFO : org.springframework.cache.ehcache.EhCacheManagerFactoryBean - Initializing EHCache CacheManager DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'findByRecipient' with attribute: [CacheableOperation[public java.util.List com.github.springmvcdemos.cachedemo.domain.MessageRepository.findByRecipient(java.lang.String)] caches=[messageCache] | condition='' | key=''] DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'createMessage' with attribute: [CacheEvictOperation[public com.github.springmvcdemos.cachedemo.domain.Message com.github.springmvcdemos.cachedemo.domain.MessageRepository.createMessage(com.github.springmvcdemos.cachedemo.domain.Message)] caches=[messageCache, messagesCache] | condition='' | key='',true,false] DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'findAll' with attribute: [CacheableOperation[public java.util.List com.github.springmvcdemos.cachedemo.domain.MessageRepository.findAll()] caches=[messagesCache] | condition='' | key='']Please note that the method will be cached using default
key
– meaning that the method parameter is considered as a key and default cache condition
– meaning that the method is always cached. Consult API docs for more details regarding these attributes. When calling the method for the first time we may observe in logs that the method was actually executed (hibernate.show_sql
property is set totrue
):Hibernate: select message0_.id as id0_, message0_.body as body0_, message0_.recipient as recipient0_ from Message message0_ where message0_.recipient=?Another call will not execute the method, instead its result will be returned from cache. And that’s it!
Summary
Spring cache abstraction brings to developers the possibility of method caching in applications with a minimal code changes and simple configuration to control the cache behavior. Comparing to already mentioned Ehcache Annotations for Spring it has one big advantage in my opinion: Spring provides an abstraction, which makes it extensible to support more cache managers in future or to provide your own ones.References
- Quick start with Ehcache Annotations for Spring – the original article describing method caching using ehcache annotations for Spring project.
- Spring reference documentation
출처 - http://blog.goyello.com/2012/01/20/quick-start-with-methods-caching-using-spring-3-1-and-ehcache/
Spring 3.1 M1 is out with some very useful features. One of the coolest feature in the latest release is comprehensive Caching support!
Spring Framework provides support for transparently adding caching into an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.
The cache is applied to Java methods, reducing the number of executions based on the information available. Spring checks if the given method is already executed for given set of parameters. If the method is already executed, Spring uses the cache value and returns it to caller instead of calling the method again. This is a write through cache. 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.
Adding Cache support to Spring project
In order to add Cache support to any Spring based project, one needs to declare the configuration using new Spring tag in the schema declaration.
< 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 /> ... </ beans > |
Note the cache:annotation-driven
tag in above declaration enables the caching in given Spring project.
Using @Cacheable and @CacheEvict annotations
Spring 3.1 M1 provides two very useful Java annotations: @Cacheable
and @CacheEvict
which allow methods to trigger cache population or cache eviction. Let us take a closer look at each annotation:
@Cacheable annotation
This annotation mark a method cacheable. Thus the result from this method call will be stored into the cache on subsequent invocations with same arguments.
@Cacheable ( "persons" ) public Person profile(Long personId) { ... } |
In the above code snippet, method profile
is marked cacheable using @Cacheable
annotation. Also the method is associated with a cache named “persons
“. Whenever method profile is called, the Spring framework will check if cached entry is available in persons cache and returns the same without calling profile method.
It is also possible to provide multiple cache names if you have multiple caches declared in your application. For example:
@Cacheable ({ "persons" , "profiles" }) public Person profile(Long personId) { ... } |
In above code snippet, we provide two cache names persons and profiles. Spring framework will check in all the caches if entry is available for given method call with argument personId, if at least one cache is hit, then the associated value will be returned.
@CacheEvict annotation
Cache eviction is removing of any unused or stale data from the cache. Opposed to @Cacheable, annotation @CacheEvict demarcates methods that perform cache eviction, that is methods that act as triggers for removing data from the cache. Just like its sibling, @CacheEvict requires one to specify one (or multiple) caches that are affected by the action, allows a key or a condition to be specified but in addition, features 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 = "persons" , allEntries= true ) public List<Person> listPersons() |
This annotation is very useful when an entire cache region needs to be cleared out. The Spring framework will ignore any key specified in this scenario as it does not apply.
Using Default key
The cache is nothing but a key-value store which stores the data based on certain key. In Spring framework based caching, the method arguments of cached method acts as the source of Key generation. Every key is essentially the Hash-code of these arguments. This approach works well for objects with natural keys as long as the hashCode()
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.
To provide a different default key generator, one needs to implement theorg.springframework.cache.KeyGenerator
interface. Once configured, the generator will be used for each declaration that does not specify its own key generation strategy.
By default, all the method arguments are used in Key generation logic. In practice not all methods have only one argument or, worse yet, the parameters are not suitable as cache keys – take for example a variation of the method above:
@Cacheable (value= "persons" , key= "personId" ) public Person profile(Long personId, Long groundId) { ... } |
Here we are using just personId
in key generation ignoring groupId altogether.
Understand Conditional caching
Spring framework also supports conditional caching letting user to cache certain methods based on some conditions. For example, in following code snippet we cache profiles only for those users who have profileId greater than 50:
@Cacheable (value= "persons" , condition= "personId > 50" ) public Person profile(Long personId) { ... } |
Currently supported libraries
There are probably hundreds of cache libraries available which can be used in your JEE project. For now the Spring framework supports following implementations:
- JDK ConcurrentMap based Cache
- Ehcache based Cache
JDK ConcurrentMap based Cache
The JDK-based Cache implementation resides under org.springframework.cache.concurrent
package. It allows one to use ConcurrentHashMap
as a backing Cache store.
<!-- generic cache manager --> < bean id = "cacheManager" class = "org.springframework.cache.support.SimpleCacheManager" > < property name = "caches" > < set > < bean class = "org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name = "default" /> < bean class = "org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name = "persons" /> </ set > </ property > </ bean > |
In above code snippet, we use SimpleCacheManager
class to create a CacheManager
. Note that we have created two caches in our application, one is default
and second is persons
.
Ehcache based Cache
The Ehcache implementation is located under org.springframework.cache.ehcache
package. Again, to use it, one simply needs to declare the appropriate CacheManager
:
< bean id = "cacheManager" class = "org.springframework.cache.ehcache.EhcacheCacheManager" p:cache-manager = "ehcache" /> <!-- Ehcache library setup --> < bean id = "ehcache" class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location = "ehcache.xml" /> |
This setup bootstraps ehcache library inside Spring IoC (through bean ehcache) which is then wired into the dedicated CacheManager implementation. Note the entire ehcache-specific configuration is read from the resource ehcache.xml.
References
Related Posts
출처 - http://viralpatel.net/blogs/cache-support-spring-3-1-m1/
TUESDAY, OCTOBER 4, 2011
EhCache Integration with Spring and Hibernate. Step by Step Tutorial
Important point to note here is that Cache Manager Name and its Configuration Location. We can use cacheManager in our code to store and retrieve information from Cache.
Next step is to create Controller. Customer Controller is very simple. I am using Spring Restful Web services to retrieve Customer Information. In my Controller I have no logic related to EhCache. I just have two versions of getCustomer method (one with Annotation and one without Annotation), save Customer and Clear Cache method. Code is simple and self explanatory for any Spring MVC developer.
To Clear Cache we can just use TriggerRemove Annotation
https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B0YFdqXJcI3mY2ZiYzA4NWQtNmQ0ZS00ZWM0LTlkMzktMmM3YmJmZjUzNDEy&hl=en_US
Hibernate uses different type of Caches
Put ehcache.xml at your classpath. It could be in classes or WEB-INF folder
In you Entity just add Annotations related to ehcache and thats it.
Complete Source Code is present at following location
https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B0YFdqXJcI3mMDJiMDU4MmUtZWFkYy00MTAxLWFjMmYtMWYyMGNhMWY2Mjdj&hl=en_US
출처 - http://eiconsulting.blogspot.kr/2011/10/ehcache-implementation-in-spring.html
Cache abstraction in Spring 3
01/07/2012 9 COMMENTS
In this article we’ll have a look at the new Spring 3 feature: caching by annotation.
I Cache abstraction layer
Caching is an important feature for all applications needing high performance. Many open-source frameworks are available: EhCache, JBoss Cache, OSCache, Open Terracota …
Their integration with the application is sometimes easy sometimes more complex, depending on the desired features and use (distributed cache ? cache JMX management …)
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.
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 CacheManager interface. By default 2 concrete implementation of CacheManager are provided:
- EhCacheCacheManager: default implementation for EhCache
- ConcurrentMapCacheManager: default implementation using Java ConcurrentHashMap as cache store
II Configuration
For this example we’ll use EhCache as cache implementation.
First you need to import the Spring cache namespace and add the &th;cache:annotation-driver> tag.
1 | < cache:annotation-driven cache-manager = "cacheManager" mode = "proxy" order = "1" /> |
- cache-manager: id or name of the bean implementing the CacheManager interface. Can be omitted because by convention Spring will look for any bean with the name “cacheManager“
- mode: proxy or aspectj. “proxy” will use Spring AOP framework to proxy any class having a caching annotation (see below). “aspectj” will rely on AspectJ aspect for cache management
- order: 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
Then you need to declare the cacheManager bean and implementation instance (or factory bean)
1 2 3 4 5 6 7 8 | < bean id = "cacheManager" class = "org.springframework.cache.ehcache.EhCacheCacheManager" > < property name = "cacheManager" ref = "ehcache" /> </ bean > <!-- Ehcache library setup --> < bean id = "ehcache" class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > < property name = "configLocation" value = "classpath:ehcache.xml" /> </ bean > |
First we declare the EhCacheCacheManager as default cache manager. Then we inject theEhCacheManagerFactoryBean so it can retrieve an instance of EhCache class.
Please notice the very confusing naming. The EhCacheCacheManager instance is given the id “cacheManager” as per Spring convention but it also has a cacheManager property. This property is indeed an instance ofnet.sf.ehcache.CacheManager, different from the org.springframework.cache.CacheManager interface.
Last but not least, you can specify the config file for ehcache with the property configLocation ofEhCacheManagerFactoryBean. If not declared it will default to “ehcache.xml“
III Cache annotations
By default Spring proposes several annotations to manage your caches:
- @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
And that’s pretty much. But don’t be fooled by their apparent simplicity, they offer a lot of possibilities to fine tune your caching.
A @Cacheable
When annotating a method with @Cacheable, its returned value will be put into the cache provided it meets some condition (if any). Consequently it does not make sense to annotate a void method.
So what can be put in cache ? Pretty much anything, an Object, a collection (List, Map, Set, Array)…
When is the cache activated ? All subsequent calls on the same method with the same arguments or cache key (we’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.
Below is the pseudo-code:
- Method is called with arguments args
- Use the args hashCode or extract the cache key from the args to look for an entry in the cache
- If a corresponding entry is found
- Return the cached entry
- Else
- Execute really the method
- Put the method returned value into the cache using the args hashCode of extracted cache key
So what is the cache key and why do we need to use args hashCode ? We’ll see it shortly. First the @Cacheableannotation offers the following attributes:
- value: mandatory, the name of the cache to work on
- key: optional, the key used to store and fetch data from the cache
- condition: optional, specifies the condition to verify before caching an item
Example:
1 2 3 4 5 6 7 8 9 | @Cacheable (value = "user-cache" , key= "#userSearchVO.name" , condition= "#supportUser == false" ) public User findUser(UserSearchVO userSearchVO, boolean supportUser) { User foundUser = null ; ... ... ... return foundUser; } |
In the above example
- value = “user-cache” indicates the name of the cache in which entries are stored
- key=”#searchVO.name” defines the key to lookup into the cache. Here it is the name property of theUserSearchVO object
- condition=”#supportUser == false” provides the condition to trigger the cache. The cache lookup and the method result caching are triggered only when the supportUser flag is set to false
Please notice that for the key and condition attributes, we are using SpEL to process the method arguments.
This is very convenient because the SpEL expression language is extremely powerfull. For example, if theUserSearchVO has an attribute searchType of enum type, we can do the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Cacheable (value = "user-cache" , key= "#userSearchVO.name" , condition= "#userSearchVO.searchType == ${T(com.doan.spring.cache.SearchType).CLASSIC" ) public User findUser(UserSearchVO userSearchVO, boolean supportUser) { User foundUser = null ; ... ... ... return foundUser; } public enum SearchType { CLASSIC, ADVANCED, SUPPORT } |
In this case the cache is triggered only when the search is of type CLASSIC.
Note that we use == instead of equals() to check for the searchType. Since the attribute searchTypeis an enum, we want to check object identity (same reference) with == rather than object equality (same value) with equals().
Another simple example:
1 2 3 4 5 6 7 8 9 | @Cacheable (value = { "user-cache1" , "user-cache2" }) public User findUserByLoginAndName(String login, String name) { User foundUser = null ; ... ... ... return foundUser; } |
In this example, we declare more than one cache (“user-cache1″,“user-cache2″) and no key information.
By default if no cache key is provided Spring will compute a hash code of all method arguments (here it is login &name) and use it as a key for cache lookup. 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.
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, subsequent caches are skipped. If all caches are scanned and no entry is found, the method will be executed and the result added to all the declared caches.
B @CacheEvict
The @CacheEvict is used to trigger explicit cache eviction. By default most of caching frameworks expire the cache data after some defined duration. The @CacheEvict annotation is usefull when you want to control explicit cache eviction upon some method calls.
1 2 3 4 5 6 | @CacheEvict (value = "user-cache" , key = "#user.login" ) public void updateUser(User user) { ... ... } |
The above code is quite self-explanatory. The @CacheEvict annotation exposes the following attributes:
- value: mandatory, the name of the cache to evict entry
- key: optional, the key used to lookup data from the cache
- condition: optional, specifies the condition to verify before evicting a cache entry
- allEntries: optional, indicates that all entries from the cache should be removed
- beforeInvocation: optional, indicates whether the cache evict should be done before or after method call
Obviously the key and allEntries attributes are mutually exclusive.
C @CachePut
The @CachePut annotation allows you to “update” a cache entry. This is very similar to @Cacheable but for entry update. The @CachePut annotation has exactly the same attributes than @Cacheable.
1 2 3 4 5 6 7 8 9 10 | @CachePut (value = "user-cache" , key = "#user.login" ) public User updateUserName(User user,String newName) { ... ... user.setName(newName); ... ... return user; } |
In the case of @CachePut, the method is always executed and its returned result is put into the cache, using the provided key (or arguments hash), replacing the old entry if necessary.
The only case where the method is not executed is when you provide an optional @CachePut condition and the condition is not met.
IV Multiple caching policies
Let’s suppose than we have an application with 2 cache regions: “user-cache” and “user-details-cache”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Cacheable (value = "user-cache" , key= "#login" ) public User findUserByLogin(String login) { ... ... return foundUser; } ... ... @Cacheable (value = "user-details-cache" , key= "#login" ) public UserDetails findUserDetailsByLogin(String login) { ... ... return userDetails; } |
It is possible to trigger the eviction from both caches with the same key:
1 2 3 4 5 6 7 8 9 10 | @CacheEvict (value = { "user-cache" , "user-details-cache" }, key= "#login" ) public void updateUserDetails(String login, UserDetails newUserDetails) { ... ... } |
In this particular example, it’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 ?
1 2 3 4 5 6 7 | @CacheEvict (value = "user-cache" , key= "#login" ) @CacheEvict (value = "user-details-cache" , key= "#newUserDetails.id" ) public void updateUserDetails(String login, UserDetails newUserDetails) { ... ... } |
It’s simply not possible because Java does not allow you to have more than one type of annotation on the same method.
For this kind of use case, Spring provides a generic @Caching annotation. This annotation simply allows you to group caching annotations of same type like @Cacheable, @CacheEvict or @CachePut.
With this said, our example becomes:
1 2 3 4 5 6 7 8 9 | @Caching (evict = { @CacheEvict (value = "user-cache" , key= "#login" ), @CacheEvict (value = "user-details-cache" , key= "#newUserDetails.id" ) }) public void updateUserDetails(String login, UserDetails newUserDetails) { ... ... } |
And you’re done!
Of course if you try to mix caching annotation of different type in the same @Caching, needless to say that you are running into big trouble…
V Object mutability gotcha
So far the caching abstraction infrastructure proposed by Spring is very convenient. However, for having used it in a real project, I’ve spotted 1 pain point : object mutability issues, thought this point is not Spring’s specific but common to all caching frameworks.
Indeed when you get an instance of User after calling findUserByLogin(String login), this instance may come from the cache directly.
If you are modifying this User instance (changing an user property for example), you are indeed modifying the object which is in the cache directly !!!
The immediate consequence is that on the same server, if another client looks for the same user, the cache will give him the same User instance that has been modified earlier…
There are 2 solutions for this issue.
- The first idea is to use objects returned from cache as read-only but this rule cannot be enforced easily. Suppose that you put @Cacheable annotation on a Repository or DAO method, the developper that calls this method from Service layer may not be aware that he’s getting a cached instance and may modify it.
- 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 Object Immutablity) and the deep copy should be done by thecaller (Service layer in our example). The same issue with “knowing that we’re dealing with cached instances” mentioned above also applies.
Fortunately, latest EHCache versions provide a very usefull feature: copyOnRead.
1 2 3 4 5 6 7 8 9 | < cache name = "user-cache" ... eternal = "false" timeToIdleSeconds = "10" timeToLiveSeconds = "1800" overflowToDisk = "false" copyOnRead = "true" > </ cache > |
With this flag turned on, EHCache will return you a deep copy of the object instance that is in the cache, not the instance itself. So mutability issue is solved.
Of course there is no free lunch, this convenient feature comes with its counterparts, the cost of serialization/deserialization because the default implementation is plain Java object serialization.
출처 - http://doanduyhai.wordpress.com/2012/07/01/cache-abstraction-in-spring-3/
@Caching With Multiple Keys
I have a service that takes in a DTO and returns some result:
Bar is as follows (simplified):
I want to use @Cacheable on the foo method; however, I want to hash on the id and name properties, but not baz. Is there a way to do this? | ||
1 | Yes, you can specify using a Spring-EL expression along these lines:
or define a modified hashCode on bar and call that:
|
출처 - http://stackoverflow.com/questions/13381731/caching-with-multiple-keys
'Framework & Platform > Spring' 카테고리의 다른 글
spring data - mongoTemplate CRUD example (0) | 2013.05.29 |
---|---|
spring data - mongodb 설정 예제 (0) | 2013.05.19 |
spring - PropertyPlaceholderConfigurer(Property 값 읽어오기) (0) | 2013.03.10 |
spring oxm (0) | 2013.01.10 |
spring java based configuration (0) | 2013.01.06 |