Spring 3.1 Cache Abstraction Tutorial

One of the new features introduced in the forthcoming Spring 3.1 version is the one of cache abstraction.

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.

At its core, the abstraction applies 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 behaviour checking whether the method has been already executed for the given arguments. If it has, then the cached result is returned without having to execute the actual method; if it has not, 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.

This concept is of course not something new. You can check out Spring, AspectJ, Ehcache Method Caching Aspect a very interesting post from Brian Du Preez, one of our JCG partners, in which Aspect Oriented Programming is used.

As its name implies, Cache Abstraction is not an actual implementation, so it requires the use of an actual storage to store the cache data. As you might have guessed, 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.

Now, let’s see some sample code on caching abstraction. For this purpose, I will use the very informative Cache Abstraction in Spring 3.1.0.M1 post by James Carr, another of our JCG partners. Make sure to bookmark the Spring Cache package Javadocs along the way.

(NOTE: The original post has been slightly edited to improve readability)

Another new feature released yesterday came in parallel with me trying out some annotation based caching strategies. Caching Abstraction basically takes convention from an existing project and makes it part of Spring core.

Essentially it introduces a new interface, CacheManager, which can be implemented by a specific cache implementation. From there it adds a few new annotations to make methods cacheable. Here’s an example using my previous posts objects.

package com.jamescarr.example;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;

@Repository
public class MemoryMessageRepository implements MessageRepository {

    private static final Logger LOG =
           LoggerFactory.getLogger(MemoryMessageRepository.class);

    private final Map<String, Message> messages = 
           new ConcurrentHashMap<String, Message>();

    @Cacheable("message")
    public Message getMessage(String title){
        LOG.info("Fetching message");
        return messages.get(title);
    }
    @CacheEvict(value="message", key="message.title")
    public void save(Message message){
        LOG.info("Saving message");
        messages.put(message.getTitle(), message);
    }
    public Collection<Message> findAll() {
        return messages.values();
    }
    
    @PostConstruct
    public void addSomeDefaultMessages(){
        save(new Message("Hello", "Hello World"));
        save(new Message("Appointment", "Remember the milk!"));
    }
    
}

Here you’ll notice that the finder method has a @Cachable annotation on it with a name that specifies the cache to store to. It can also use additional attributes, for example a key which uses an expression language to determine a key from the arguments that are passed in. The default is the value of all the method arguments. On the save method I use @CacheEvict to remove the cached element from the cache if it already exists.

This of course won’t work on it’s own, so you’ll have to enable it yourself (which is good… the last thing you need is to discover a production app caching things it shouldn’t be caching). Sadly as of the time of this writing I haven’t discovered how to do this in non-xml, so here is the spring xml file to enable it and use ehcache as the implementation.

<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"
        xmlns:p="http://www.springframework.org/schema/p"
        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 />
  
        <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:com/jamescarr/example/ehcache.xml"/>
 </beans>

The ehcache configuration:

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <cache name="message"
       maxElementsInMemory="100"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"/>
    
</ehcache>

And finally adding this to the AppConfiguration, which includes doing a simple @ImportResource.

package com.jamescarr.configuration;
import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import com.jamescarr.example.MessagePrinter;

@Configuration
@ComponentScan("com.jamescarr.example")
@ImportResource("classpath:com/jamescarr/example/cache-context.xml")
public class AppConfig {
        @Autowired
        private MessagePrinter messagePrinter;
        @PostConstruct
        public void doSomething(){
                messagePrinter.printMessage("Hello");
                messagePrinter.printMessage("Hello");
                
        }
        public static void main(String[] args) {
                new AnnotationConfigApplicationContext(AppConfig.class);
        }
}

When running this example there should be a log message for the first time the method is hit, then it is not seen the second time (since it is being pulled from the cache. This is definitely pretty awesome for implementing Memoization for methods that might just have some CPU intensive computations (but give the exact expected results given a set of of inputs). I’m excited about doing some more work in this area… I’ve done method level caching before (it’s common) but it is awesome to be able to use it without having to DIY.

That’s it guys. A straightforward guide to get you started with Spring’s Cache Abstraction from James Carr. Don’t forget to share!

Related Articles:

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

One Response to "Spring 3.1 Cache Abstraction Tutorial"

Leave a Reply


9 × = eighteen



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books