Oct 6, 2011
Making things @Cacheable
Performance optimizing with caching frameworks, or in general caching, like OSCache, EHCache is inevitable in software development and especially critically important in web development. If you want to serve contents from your data storages and if you’ve hunderds of requests – per minute – or even more, like on high-traffic portals, then it must be considered to cache your contents, objects, method calls, etc. in every layer efficiently. Most of ORM frameworks like Hibernate do support first-level and second level caching out-of-box and there are also many other caching strategies like distrubuted caching with Oracle Coherence or Jboss distributed caching facilities. But we won’t discuss in detail all of them. The aim of this post is to show application developers how to integrate custom caching mechanism easily using java annotations (Java 1.5+) from programmers’ point of view.
Think about the situation that you’re developing a website of which contents are supplied by a commercial partner in xml and you have to render these contents on-fly over a reliable connection and responsive server – or you can cache the contents in a relational database, but to reduce database request it’s a best practice to integrate custom caching in your service layer. Say, the contents are about weather informations from stations and you’re about to build a weather web application. Everytime your page gets called, without any caching mechanism your service has to call partner service also. It can bring about an overhead on your network and load on backend. Maybe just for that reason, you might rehandle your contract.
EHCache is a pretty good solution and a well known caching framework which’s broadly used to cache “data” programmaticaly – not only- creating and implementing your own CacheKeys. It is easier to integrate into spring web applications. But, things’re getting even better with ehcache-spring-annotations project on google code if the application deveper had a chance to cache some data, supplying some meta informations (god bless annotations) without any coding effort.
The common scenario is that we have a MVC application and the following controller handles user requests on “http://{yourhost.com}/{appcontext}/weatherchannel/index.html?sid=4567
“sid” stands for “station id” and passed as GET parameter to the controller.
@Controller @RequestMapping("/weatherchannel") public class WeatherChannelController { @Autowired private IWeatherDataGateway weatherGateway; @RequestMapping(value="/index.html", method=RequestMethod.GET) public String handleUserRequest(@RequestParam("sid") String stationId, ModelMap model) { List<WeatherStationInfoBean> stationInfos = weatherGateway.callPartnerEndpointRestfully(id); model.addAttribute("sinfos", stationInfos); return "stationinfo"; } }
Our service instance “WeatherDataGateway” will be injected by spring framework via dependency injection and the service method “callPartnerEndpointRestfully” will be called with sid parameter and the results’re put into the model to be rendered in view “stationinfo”. Following class diagram demonstrates our application:

Implementation details of service method “callPartnerEndpointRestfully” is omitted to isolate our problem domain from the whole. You can imagine that the method makes calls over HTTP on a backend service and unmarshalls the JSON or XML response from backend and returns with a list of entities called WeatherStationInfoBean subsequently. It is a good place where we can use caching to reduce backend traffic and increase our service responsiveness. To make something cachable we use @Cacheable annotation with cache name.
// inside IWeatherDataGateway.java public interface IWeatherDataGateway { public List<WeatherStationInfoBean> callPartnerEndpointRestfully(final String id); } // inside WeatherDataGateway.java public class WeatherDataGateway implements IWeatherDataGateway { @Cacheable("weatherCache") public List<WeatherStationInfoBean> callPartnerEndpointRestfully(final String id) { // service logic } @Cacheable("weatherCache") public List<WeatherStationInfoBean> callPartnerForStationListings() { // service logic } }
| Warning: You can make just the methods cacheable with @Cacheable annotation which’ve public visibilty. Otherwise the magic won’t work and even you won’t get any error in your log files. |
Required cache name identifies which cache configuration we want to use from EHCache configuration file which must lay on classpath:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/> <cache name="weatherCache" maxElementsInMemory="100" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU" /> </ehcache>
Default cache behaviour is “defaultCache” and our custom cache is determined under “weatherName”. The attributes of cache configuration are self-explanatory except memoryStoreEvictionPolicy. This attribute defines a policy which’s enforced upon reaching the maxElementsInMemory limit. Available strategies are “LRU” (Least Recently Used), “LFU” (Less Frequently Used) or FIFO.
If you want to find more about EHCache and configuration, you can refer to official documentation.
Now it’s time to get @Cachaeble work with spring framework.
<!-- from the file 'context.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:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd"> <ehcache:annotation-driven cache-manager="ehCacheManager" /> <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/> <bean id="weatherDao" class="com.bagdemir.dao.WeatherDao"/> </beans>
Using
<ehcache:annotation-driven/> we enable @Cacheable annotation. It is most common mistake which’s made by programmer to forget this. It is not easy to understand why it doesn’t work and find out the cause of the problem, because there is no error output in logs at all. There are some attributes that’re used with like “cache-manager”. “cache-manager” is optional as long as the cache manager bean
has the name “cacheManager” which you want to wire.
create-missing-caches is set “true” explicitly (default value is “false”) and if the caching name which’s defined in @Cacheable can’t be found, then it will be created a new one according to defaultCache. An interesting attribute is “proxy-target-class” which defines wheather interface- based (JDK based) proxies or class- based (CGLib based) proxies are used to proxy the target object. If it is set to true, then class-based proxies are used, otherwise or it is omitted interface- based proxies.
Spring framework’s ProxyFactoryBean creates a JDK-based proxy if the target class does implement an interface, otherwise CGLib proxies. If you plan to annotate class public methods with @Cacheable which implement no interface at all, then you must enable proxy-target-class.
Key Management
default-cache-key-generator, defines which cache key generator will be used default. If there is no default setting, HashCodeCacheKeyGenerator is used. There’re much more cache key generators like StringCacheKeyGenerator, ListCacheKeyGenerator, MessageDigestCacheKeyGenerator :
@Cacheable(cacheName="weatherCache", keyGenerator=@KeyGenerator(name="StringCacheKeyGenerator")) public String weatherStatusFor(String city) { // some code }
The keys created by StringCacheKeyGenerator are in human readable form using toString() method of argument objects and composed with theirvalues. HashCodeCacheKeyGenerator generates a simple 64bit hash code based on the method arguments.
ListCacheKeyGenerator builds a cache key converting all arrays to the Lists and returns a immutable, serializable List of arguments as key. The generator creates keys quickly as compared to checking equality, because every time the equality of objects wanted to be checked, the equality of all elements in the list must be considered which effect on key equality. There is also MessageDigestCacheKeyGenerator which works actually like HashCodeCacheKeyGenerator, but instead accumulating the hash code in a long a MessageDigest is used. This results in a much larger key space and the use of a cryptographic hashing function for better key distribution[1].
Removing Elements from Cache
@TriggersRemove is used to remove the elements from cache. We can either remove one certain element or all elements instead. The latter is easy to implement. Say, we have a new method to delete persistent objects from database and it is called, deleteWeather, something like this:
@Cacheable("weatherCache") public List<WeatherStationInfoBean> callPartnerEndpointRestfully(final String id) { // service logic } @Cacheable("weatherCache") public List<WeatherStationInfoBean> callPartnerForStationListings() { // service logic } @TriggersRemove(cacheName="weatherCache", removeAll=true) public Weather deleteWeather(String zipCode) { // DB operations to delete } }
Removing a particular element from the cache is tricky because to find an element (according to equals/hashCode) in cache depends on cache keys and equality of keys but the method names are also included in keys while key calculation. We have to exclude the usage of method names like this:
@Cacheable(cacheName="weatherCache", keyGenerator = @KeyGenerator ( name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ) ) ) ) public List<WeatherStationInfoBean> callPartnerEndpointRestfully(final String id) { // service logic } @TriggersRemove(cacheName="weatherCache", keyGenerator = @KeyGenerator ( name = "HashCodeCacheKeyGenerator", properties = @Property( name="includeMethod", value="false" ) ) ) ) public Weather deleteWeather(String zipCode) { // DB operations to delete } }
@Property( name=”includeMethod”, value=”false” ) says that the method names won’t be used in key generation.
Conclusion
Being able to use ehcache and in general caching mechanism in your code with annotations is a pretty good idea. As a developer you can bring the power of EHCache caching framework into your code without any extra programming effort. You can save your time and concentrate on your business logic or design.
PS: To use Cacheable annotation in your project you need the following maven dependency:
<dependency> <groupId>com.googlecode.ehcache-spring-annotations</groupId> <artifactId>ehcache-spring-annotations</artifactId> <version>1.1.3</version> </dependency>
[1] http://code.google.com/p/ehcache-spring-annotations/wiki/UsingTriggersRemove

Recent Comments