<cache:annotation-driven />
...together with the appropriate schema definition in your beans XML element declaration:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
...with the salient lines being:
xmlns:cache="http://www.springframework.org/schema/cache"
...and:
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
However, that’s not the end of the story, as you also need to specify a caching manager and a caching implementation. The good news is that if you’re familiar with the set up of other Spring components, such as the database transaction manager, then there’s no surprises in how this is done.
A cache manager class seems to be any class that implements Spring’s org.springframework.cache.CacheManager interface. It’s responsible for managing one or more cache implementations where the cache implementation instance(s) are responsible for actually caching your data.
The XML sample below is taken from the example code used in my last two blogs.
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="employee"/> <!-- TODO Add other cache instances in here --> </set> </property> </bean>
In the above configurtion, I’m using Spring’s SimpleCacheManager to manage an instance of their ConcurrentMapCacheFactoryBean with a cache implementation named: “employee”.
One important point to note is that your cache manager MUST have a bean id of cacheManager. If you get this wrong then you’ll get the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cache.interceptor.CacheInterceptor#0': Cannot resolve reference to bean 'cacheManager' while setting bean property 'cacheManager'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cacheManager' is defined at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:106) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517) : : trace details removed for clarity : at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cacheManager' is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:277) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:322)
As I said above, in my simple configuration, the whole affair is orchestrated by the SimpleCacheManager. This, according to the documentation, is normally “Useful for testing or simple caching declarations”. Although you could write your own CacheManager implementation, the Guys at Spring have provided other cache managers for different situations
- SimpleCacheManager - see above.
- NoOpCacheManager - used for testing, in that it doesn’t actually cache anything, although be careful here as testing your code without caching may trip you up when you turn caching on.
- CompositeCacheManager - allows the use multiple cache managers in a single application.
- EhCacheCacheManager - a cache manager that wraps an ehCache instance. See http://ehcache.org
Selecting which cache manager to use in any given environment seems like a really good use for Spring Profiles. See:
And, that just about wraps things up, although just for completeness, below is the complete configuration file used in my previous two blogs:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!-- Switch on the Caching --> <cache:annotation-driven /> <!-- Do the component scan path --> <context:component-scan base-package="caching" /> <!-- simple cache manager --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="employee"/> <!-- TODO Add other cache instances in here --> </set> </property> </bean> </beans>
As a Lieutenant Columbo is fond of saying “And just one more thing, you know what bothers me about this case...”; well there are several things that bother me about cache managers, for example:
- What do the Guys at Spring mean by “Useful for testing or simple caching declarations” when talking about the SimpleCacheManager? Just exactly when should you use it in anger rather than for testing?
- Would it ever be advisable to write your own CacheManager implementation or even a Cache implementation?
- What exactly are the advantages of using the EhCacheCacheManager?
- How often would you really need CompositeCacheManager?
6 comments:
I'd suggest that someone would use ehcache in favor of bigger caches. The SimpleCacheManager is memory only (I'd expect a set of Hashmaps or LRUmaps).
A classic "it depends on..." thing.
Knalli,
The questions were somewhat rhetorical. See my Caching and Ehcache blog.
Hi Roger,
Have you noticed that the ConcurrentMapCache locks every once in a while? I am using it with the SimpleCacheManager and notice that sometimes there is a 10 second gap while using the cache's get() method under heavy load/activity.
If so, how would you get around it?
Thanks,
Sami
I've not seen this issue, maybe I haven't put it under heavy enough load. Perhaps it would be a good idea to raise it on the Spring forum so that the Guys at Spring could take a look.
Hi Roger, Caching is not working in my case with similar config as you mentioned. Can you please look at my stackoverflow question http://stackoverflow.com/q/18872643/432903.
Thank you
Prayag
I see that you've already answered your question
http://stackoverflow.com/questions/18872643/spring-data-hibernate-query-caching-not-working
Post a comment