Learn how to use Ehcache 3 and JSR-107 to speed up your Spring 4.x MVC applications!

In this post I would like to demonstrate how to use Ehcache 3.0 as Spring’s caching implementation. This article will show you how to use Ehcache 3.0, Spring 4.2 and the JSR-107 annotations to build a simple Spring web application. I have also included some background reading for those of you that would like to read more in depth on the subject matter.

JSR-107(JCache) Annotations

If you are familiar with Spring, you know that it provides annotations to assist in developing applications. In regards to caching, Spring offers support for two sets of annotations that can be used to implement caching. You have the original Spring annotations and the new JSR-107 annotations. The original Spring annotations are available to use with Spring versions 3.1+, while the JSR-107 annotations are only available in Spring 4.1+. In this example we are going to use the JSR-107 annotations. Below I have listed the most commonly used JSR-107(JCache) annotations, with brief descriptions as well as links to their API’s.

Remember that the value is not cached forever. The length of time will be influenced by how you set your eviction policy, TTL and TTI. Please see this discussion for the difference between TTL and TTI: http://stackoverflow.com/questions/2583429/how-to-differentiate-between-time-to-live-and-time-to-idle-in-ehcache

Steps

Let’s get started! Below are a few quick steps to get you up and running with Ehcache 3, Spring 4.1+ and JSR-107. The entire example can be found on GitHub at https://github.com/gibsong/ehcache-jsr107-spring

  1. Create a maven Spring project. Maven 3.2 or greater is required.

  2. Add Ehcache 3 to your pom.xml.

    pom.xml
      <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.0.0</version> (1)
      </dependency>
    1 Be sure to substitute the version number above with the version number of Ehcache that you want to use.
    The Ehcache 3 jar must be on the classpath! Remove all existing caching provider jars from the classpath to ensure that the right implementation is used.
  3. Add the jar for the JSR-107 API to the pom.xml

    pom.xml
      <dependency>
        <groupId>javax.cache</groupId>
        <artifactId>cache-api</artifactId>
      </dependency>
  4. Add Spring boot jars

    pom.xml
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId> (1)
        <version>1.3.2.RELEASE</version>
      </parent>
      ...
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-cache</artifactId> (2)
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId> (3)
        </dependency>
        ...
      <dependencies>
    1 Add the spring-boot-starter-parent parent project to use Spring boot
    2 Add the spring-boot-starter-cache to use Spring Framework’s caching support
    3 Add spring-boot-starter-web to use Spring MVC
  5. Set the spring.cache.jcache.config property to include the classpath and ehcache.xml file. This needs to be done in the application.properties file.

    application.properties
    spring.cache.jcache.config=classpath:ehcache.xml
  6. Enable caching. This can be done in 1 of 2 ways:

    1. Enable with @EnableCaching annotation:

      SpringJsr107Ehcache3Application.java
        @EnableCaching
        public class SpringJsr107Ehcache3Application
        {
          public static void main(String[] args)
          {
              SpringApplication.run(SpringJsr107Ehcache3Application.class, args);
          }
        }
    2. Or enable from the Spring xml configuration file by adding the following tag: <cache:annotation-driven />

        <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>
  7. Declare caching on a method by adding the @CacheResult annotation.

    PersonService.java
      @CacheResult (1)
      Person getPerson(int ssn)
      {
        switch (ssn)
        {
          case 123456789:
            return new Person(ssn, "Geoff", "Gibson");
          case 987654321:
            return new Person(ssn, "Cory", "Beck");
          default:
            return new Person(ssn,"John","Doe");
        }
      }
    1 Add the @CacheResult annotation above the method. In this case the key is the "int ssn" parameter and the value cached is a Person instance. So if you call this method with ssn="123456789", the Person(ssn, "Geoff", "Gibson") will be returned and cached. The next time the getPerson(…​) method is called with ssn="123456789" (assuming the key/value wasn’t evicted from the cache) the method won’t run and instead it will grab Person(ssn, "Geoff", "Gibson") from the cache and return it.
  8. Configure ehcache.xml

    ehcache.xml
    <config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'  (1)
        xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>  (2)
    
      <service>
        <jsr107:defaults>
          <jsr107:cache name="people" template="heap-cache"/> (3)
        </jsr107:defaults>
      </service>
    
      <cache-template name="heap-cache">
        <listeners>    (4)
          <listener>
            <class>org.terracotta.ehcache.EventLogger</class>
            <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
            <event-ordering-mode>UNORDERED</event-ordering-mode>
            <events-to-fire-on>CREATED</events-to-fire-on> (5)
            <events-to-fire-on>UPDATED</events-to-fire-on> (6)
            <events-to-fire-on>EXPIRED</events-to-fire-on> (7)
            <events-to-fire-on>REMOVED</events-to-fire-on> (8)
            <events-to-fire-on>EVICTED</events-to-fire-on> (9)
          </listener>
        </listeners>
        <resources>
          <heap unit="entries">2000</heap> (10)
          <offheap unit="MB">100</offheap> (11)
        </resources>
      </cache-template>
    </config>
    1 The core namespace, the xsd can be found here: http://www.ehcache.org/schema/ehcache-core-3.0.xsd
    2 The JSR-107 namespace, the xsd can be found here: http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd
    3 Defines a cache with alias "people", which inherits from cache-template "heap-cache"
    4 This section allows you to add cache event listeners. I added a listener for 5 events. Each event will be logged, by the EventLogger class, when it occurs.
    5 Defines a CREATED event, when an entry is added to the cache, with this listener.
    6 Defines an UPDATED event, when an entry is updated in the cache, with this listener. However in this example this one will never be used. I just added it as an example.
    7 Defines an EXPIRED event, when an entry is expired from the cache, with this listener.
    8 Defines an REMOVED event, when an entry is removed from the cache, with this listener.
    9 Defines an EVICTED event, when an entry is evicted from the cache, with this listener.
    10 The heap is configured to allow 2000 entries
    11 The offheap storage is configured with 100 MB of space. Remember the unit of measure is case sensitive.
    XML Configuration Documentation: http://www.ehcache.org/documentation/3.0/xml.html
  9. Create a cache by implementing the JCacheManagerCustomizer.customize(CacheManager cacheManager) method, which will be invoked before the CacheManager is used.

    PersonService.java
      @Component
      public static class CachingSetup implements JCacheManagerCustomizer
      {
        @Override
        public void customize(CacheManager cacheManager)
        {
          cacheManager.createCache("people", new MutableConfiguration<>()  (1)
            .setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(SECONDS, 10))) (2)
            .setStoreByValue(false)
            .setStatisticsEnabled(true));
        }
      }
    1 Creates a cache with an alias of "people".
    2 This line sets the expiration policy. In this case we set it to 10 seconds. Thus, if an entry hasn’t been touched (created, updated, or accessed) for the last 10 seconds it will be evicted.
  10. Now you can build the project by running the following maven command: mvn clean install

  11. To run the application use this maven command: mvn spring-boot:run

  12. To make a get request to the application use the following url: http://localhost:8080/person/{ssn} IMPORTANT: Remember to replace {ssn} in the url with an integer value. 123456789 and 987654321 are mapped to unique Person instances, while anything else maps to a generic Person instance.

Conclusion

Wow wasn’t that easy! I hope you enjoyed my quick start tutorial on how to use Ehcache 3 with Spring 4.1+ and JSR-107. If you have any questions please feel free to send them to me at [email protected] and I will be happy to help out.