Skip to content

Getting Started

Jakub edited this page Jun 28, 2019 · 7 revisions

Introduction

Distributed caching can be a big, hairy, intricate, and complex proposition when using it extensively.

Simple-Spring-Memcached (SSM) attempts to simplify implementation for several basic use cases.

This project enables caching in Spring-managed beans, by using Java 5 Annotations and Spring/AspectJ AOP on top of the spymemcached, xmemcached or Amazon ElastiCache Cluster Client for Java. Using Simple-Spring-Memcached requires only a little bit of configuration and the addition of some specific annotations on the methods whose output or input is being cached.

Dependencies

The first thing you'll need, of course, is at least one running Memcached server. (Installation and usage instructions may be found on the memcached project page.)

In addition to the Simple-Spring-Memcached JAR files, the following libraries must be accessible (if maven is used you can skip it because all libraries will be included automatically):

  • Spring is an assumed dependency, and this project was built against the 4.3.9.RELEASE version. A Spring library that is 4.3.9.RELEASE compatible must included be on the classpath.
  • AspectJ library is also required, and this project was built against the 1.8.9 versions. This library may have version dependencies with your Spring version, so you are free to add whatever versions are necessary to the classpath.
  • This project uses Simple Logging Facade for Java (SLF4J), and is built against SLF4J, though you are welcome to provide any other implementation.

Next step is to choose one of the available java memcached providers. Using Simple Spring Memcached requires adding only one extra dependency to your project's pom (one of the below providers).

  • spymemcached

For spymemcached add dependency:

<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>spymemcached-provider</artifactId>
  <version>4.1.3</version>
</dependency>

It uses spymemcached 2.12.3.

  • xmemcached:

For xmemcached add dependency:

<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>xmemcached-provider</artifactId>
  <version>4.1.3</version>
</dependency>	

It uses xmemcached 2.4.6.

  • aws-elasticache:

For aws-elasticache add dependency:

<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>aws-elasticache-provider</artifactId>
  <version>4.1.3</version>
</dependency>	

It uses elasticache-java-cluster-client 1.1.1.

Configuration

You will need to inform your ApplicationContext of Simple-Spring-Memcached's configuration. This is done with an import configuration directive:

<import resource="simplesm-context.xml" />

The xml file is available in simple-spring-memcached-4.1.3.jar.

Simple-Spring-Memcached also requires an annotation to enable AOP access, which in turn requires the AOP namespace being defined:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

  <aop:aspectj-autoproxy />
       
</beans>

In order to tell Simple-Spring-Memcached about the specifics of your environment, you need to configure default client:

  • spymemcached:
<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
	   http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

  <import resource="simplesm-context.xml" />

  <aop:aspectj-autoproxy /> 

  <bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.spymemcached.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="127.0.0.1:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.CacheConfiguration">
        <property name="consistentHashing" value="true" />
      </bean>
    </property>
  </bean>
</beans>
  • xmemcached:
<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
	   http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

  <import resource="simplesm-context.xml" />

  <aop:aspectj-autoproxy />  

  <bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="127.0.0.1:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.CacheConfiguration">
        <property name="consistentHashing" value="true" />
      </bean>
    </property>
  </bean>
</beans>
  • aws-elasticache:
<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
	   http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

  <import resource="simplesm-context.xml" />

  <aop:aspectj-autoproxy /> 

  <bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.elasticache.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <!-- it's not possible to connect to AWS memcached from local/developer machine. It will work only when an application 
           is deployed on AWS cloud -->
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="xxxxx.yyyyy.zzzz.cache.amazonaws.com:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.elasticache.ElastiCacheConfiguration">
        <property name="consistentHashing" value="true" />
        <!-- in this use static configuration and do not try to fetch configuration of nodes from cluster (auto discovery node feature is disabled) -->
	<property name="clientMode" value="#{T(net.spy.memcached.ClientMode).Static}" />
      </bean>
    </property>
  </bean>
</beans>

The consistentHashing parameter refers to a method of choosing to which node a value is written, as implemented in the spymemcached or xmemcached client. The address parameter in addressProvider bean is how you tell the Simple-Spring-Memcached library upon which IPs and ports your Memcached server(s) are listening.

Usage

Any discussion of Memcached usage requires an understanding of keys and values. To get an understanding of this, see this helpful tutorial.

@CacheKeyMethod

In SSM, you are able to identify 'key objects'. These are the objects that Simple-Spring-Memcached will rely upon to generate a (non-namespaced) unique key for referring to values within the cache. For any given key object, it will first be checked if any of its methods are annotated with @CacheKeyMethod. If a @CacheKeyMethod is found, and it conforms to the required signature (no-arg, with an output of type String), this is the method that will be relied upon to generate a unique key. If there is no conforming @CacheKeyMethod, then the basic Object.toString() method will be used.
Key can be generated using more than one key object. The (non-namespaced) unique key is a concatenation of single keys generated from each key object and joined using '/'.

The Big 9 Annotations

The next 9 annotations are the real meat of this project. These are the annotations that mark where caching is to be applied. There are two dimensions that these annotations cover: cardinality (are we working with a single value with an assigned key, a single value with a calculated key, or a 'multiplexed' (collection) set of values with calculated keys); and usage pattern (read-through, update, or invalidate).

@ReadThroughSingleCache, @ReadThroughMultiCache, @ReadThroughAssignCache

These are the most frequently used annotations. They follow the standard caching flow as follows:

  1. Check if value exists in the cache. If it does, return it and exit cleanly.
  2. If not found in the cache, then ask the underlying method for the data.
  3. Before returning the data, write it out to the cache for future access.
  4. Return the calculated data.

@InvalidateSingleCache, @InvalidateMultiCache, @InvalidateAssignCache

Whereas the ReadThrough*Cache annotations are involved with populating the cache when asked for information, the Invalidate*Cache annotations effectively take information out of the cache. These annotations are useful when you know that a value has been made stale and you want to force it to refresh on the next time it is referenced in a ReadThrough*Cache method.

@UpdateSingleCache, @UpdateMultiCache, @UpdateAssignCache

The Update*Cache annotations can serve as a bit of performance improvement. Unlike the Invalidate*Cache annotations which remove a value from the cache to be looked up again in the future, the Update*Cache annotations force an update to the value in the cache which obviates the need to check the underlying method in a ReadThrough*Cache method.

Counter Annotations

Counters can be use to count methods invocation or user's actions. Memcached store counters as 64-bit no-negative integers.

@ReadCounterFromCache

Reads counter's value from cache. This annotation follows the standard caching flow as follows:

  1. Check if value exists in the cache and can be cast to number. If it does, return it and exit cleanly.
  2. If not found in the cache, then ask the underlying method for the data.
  3. Before returning the data, write it out to the cache (using incr command) for future access.
  4. Return the value (counter). Annotated method must return one of int, Integer, long or Long.

@IncrementCounterInCache, @DecrementCounterInCache

Those annotations increment or decrement by 1 counter in cache. In case of incrementing if counter doesn't exist in cache it is initialized with 1. Counter value is always no negative number and can't be decrementing below 0.

@UpdateCounterInCache

Similar to @UpdateSingleCache but sets new counter's value in cache. The value used to update counter should be of type int, Integer, long or Long.

Examples

@ReadThroughSingleCache

In this example, we are working with a fairly standard DAO-like pattern:

@ReadThroughSingleCache(namespace = "CplxObj", expiration = 3600)
public ComplexObject getComplexObjectFromDB(@ParameterValueKeyProvider Long complexObjectPk) {
  // ...
  return result;
}

The basic usage is that you've got a primary key (complexObjectPk) and you want to get the corresponding ComplexObject from the DB. This is kind of an expensive operation, and you'd like to do it less frequently because the data rarely changes. So, you slap on the Simple-Spring-Memcached annotation. You assign a (fairly arbitrary) namespace of "CplxObj". (Namespacing this way helps ensure that you don't have cache collisions with another type of object that may have the same primary key.) You tell Simple-Spring-Memcached that they key object (@ParameterValueKeyProvider) is complexObjectPk parameter. And lastly, you tell Simple-Spring-Memcached that you'd like for the value to stick around in the cache for at most 1 hour (3600 seconds).

@InvalidateMultiCache

Here, we deal with another fairly common DAO-like pattern:

@InvalidateMultiCache(namespace = "CplxObj")
public String saveModifiedComplexObjectsToDB(Credentials principal, @ParameterValueKeyProvider List<ComplexObject> complexObjects) {
  // ...
}

This example show off the multiplexing capabilities of SSM. Let's say you made a common change to several of the ComplexObjects, and you want to write them out to the database. This means any previously cache-stored versions of these ComplexObjects will be stale (out-of-date). We use this @InvalidateMultiCache annotation to remove cache copies of that data, which will hopefully require future calls to an @ReadThrough*Cache method to hit the underlying method for a fresh copy of the data. You'll notice that the complexObjects is annotated with @ParameterValueKeyProvider. This is because (in this made-up example) the ComplexObjects either have a method with an @CacheKeyMethod annotation that outputs the primary key, or an implementation of toString() that returns the primary key.

@UpdateAssignCache

This use-case is a bit more contrived than the above DAO-ish examples, but it gets the point across:

@ReturnDataUpdateContent
@UpdateAssignCache(namespace = "CplxObj", assignedKey = "MyFav", expiration = 7200)
public List<Long> resetMyFavoriteComplexObjectPksFromDB() {
  //...
  return results;
}

This @Update*Cache annotations are designed to force an update to the cache. This may be preferable in many cases rather than the invalidate-readthrough pattern, which ends up requiring a later trip back to the underlying method/db. This example also shows how the @*AssignCache annotations work. Instead of relying on input/output values for the key, use an annotation-declared key. The data that gets cached in this scenario is the output data (@ReturnDataUpdateContent). The list of primary keys is stored as a single value under the id "MyFav" within the namespace of "CplxObj".

Looking for more examples?

More examples are in integration test project. By default this project requires 2 memcached instances on 127.0.0.1:11211 and 127.0.0.1:11212. By default in integration tests embedded memcached (jmemcached) instances are used. The project can run with xmemcached or spymemcached:

  • xmemcached
mvn test -Pxmemcached -Dssm.defaultSerializationType=PROVIDER
  • spymemcached
mvn test -Pspymemcached -Dssm.defaultSerializationType=PROVIDER

Spring 3.1 Cache Integration

Spring 3.1 introduced new cache abstraction. Out of the box spring provides integration with two storages ConcurrentMap and ehcache. Simple Spring Memcached can be used as another storage. Using SSM users of Spring Cache can store data in memcached.

Dependency

Similar to standard usage of SSM you need to choose one of available java memcached providers.

  • spymemcached:
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>spring-cache</artifactId>
  <version>4.1.3</version>
</dependency>
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>spymemcached-provider</artifactId>
  <version>4.1.3</version>
</dependency>
  • xmemcached:
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>spring-cache</artifactId>
  <version>4.1.3</version>
</dependency>
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>xmemcached-provider</artifactId>
  <version>4.1.3</version>
</dependency>	
  • aws-elasticache:
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>spring-cache</artifactId>
  <version>4.1.3</version>
</dependency>
<dependency>
  <groupId>com.google.code.simple-spring-memcached</groupId>
  <artifactId>aws-elasticache-provider</artifactId>
  <version>4.1.3</version>
</dependency>	

Settings

If SSM is used only through Spring Cache abstraction importing simplesm-context.xml is not required but then depends-on="cacheBase" should be removed from defaultCache definition. If simplesm-context.xml isn't imported some SSM settings may not be honoured.

Annotations provided by Spring Cache abstraction (@Cacheable and @CachePut) don't allow to pass expiration time. SSM and memcached required expiration time in some operations so it has to be set somehow. The simplest way is to set default expiration time on cache while creating SSMCache object. This expiration will be used in all operations on the cache.

Memcached doesn't support cache zones or cache namespaces, so there's no such operation like clearing only some part of data stored in zone/namespace, only all data stored by memcached instance can be removed. SSM provides concept of cache zone. The name of the cache zone is mapped to the value parameter in Spring Cache annotations. Different cache zones may use common memcached servers. Such overlapping should be avoided because flushing all data from one cache zone removes also some (or all) data from another cache zone that use the same memcached instances. To protect from such case by default @CacheEvict(..., "allEntries" = true) doesn't work. To enable it set allowClear=true on SSMCache.

  • spymemcached:
<?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:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
  	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.3.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache-4.3.xsd">

  <cache:annotation-driven />
  
  <bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager">
    <property name="caches">
      <set>
        <bean class="com.google.code.ssm.spring.SSMCache">
	  <constructor-arg name="cache" index="0" ref="defaultCache" />
          <!-- 5 minutes -->
	  <constructor-arg name="expiration" index="1" value="300" />
          <!-- @CacheEvict(..., "allEntries" = true) won't work because allowClear is false, 
           so we won't flush accidentally all entries from memcached instance -->
	  <constructor-arg name="allowClear" index="2" value="false" />
	</bean>
      </set>
    </property>
  </bean>

  <bean name="defaultCache" class="com.google.code.ssm.CacheFactory" depends-on="cacheBase">
    <property name="cacheName" value="default" />
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.spymemcached.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="127.0.0.1:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.CacheConfiguration">
        <property name="consistentHashing" value="true" />
        <!-- spring can produce keys that contain unacceptable characters -->
        <property name="useBinaryProtocol" value="true" />
      </bean>
    </property>
  </bean>
</beans>
  • xmemcached:
<?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:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
  	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.3.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache-4.3.xsd">

  <cache:annotation-driven />
  
  <bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager">
    <property name="caches">
      <set>
        <bean class="com.google.code.ssm.spring.SSMCache">
	  <constructor-arg name="cache" index="0" ref="defaultCache" />
          <!-- 5 minutes -->
	  <constructor-arg name="expiration" index="1" value="300" />
          <!-- @CacheEvict(..., "allEntries" = true) won't work because allowClear is false, 
           so we won't flush accidentally all entries from memcached instance -->
	  <constructor-arg name="allowClear" index="2" value="false" />
	</bean>
      </set>
    </property>
  </bean>

  <bean name="defaultCache" class="com.google.code.ssm.CacheFactory" depends-on="cacheBase">
    <property name="cacheName" value="default" />
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="127.0.0.1:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.CacheConfiguration">
        <property name="consistentHashing" value="true" />
        <!-- spring can produce keys that contain unacceptable characters -->
        <property name="useBinaryProtocol" value="true" />
      </bean>
    </property>
  </bean>
</beans>
  • aws-elasticache:
<?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:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
  	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.3.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache-4.3.xsd">

  <cache:annotation-driven />
  
  <bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager" depends-on="cacheBase">
    <property name="caches">
      <set>
        <bean class="com.google.code.ssm.spring.SSMCache">
	  <constructor-arg name="cache" index="0" ref="defaultCache" />
          <!-- 5 minutes -->
	  <constructor-arg name="expiration" index="1" value="300" />
          <!-- @CacheEvict(..., "allEntries" = true) won't work because allowClear is false, 
           so we won't flush accidentally all entries from memcached instance -->
	  <constructor-arg name="allowClear" index="2" value="false" />
	</bean>
      </set>
    </property>
  </bean>

  <bean name="defaultCache" class="com.google.code.ssm.CacheFactory">
    <property name="cacheName" value="default" />
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.elasticache.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <!-- it's not possible to connect to AWS memcached from local/developer machine. It will work only when an application
             is deployed on AWS cloud -->
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="xxxxx.yyyyy.zzzz.cache.amazonaws.com:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.elasticache.ElastiCacheConfiguration">
        <property name="consistentHashing" value="true" />
        <!-- in this use static configuration and do not try to fetch configuration of nodes from cluster (auto discovery node feature is disabled) -->
	<property name="clientMode" value="#{T(net.spy.memcached.ClientMode).Static}" />
        <!-- spring can produce keys that contain unacceptable characters -->
        <property name="useBinaryProtocol" value="true" />
      </bean>
    </property>
  </bean>
</beans>

All classes related to integration SSM with Spring Cache (com.google.code.ssm.spring: SSMCache, SSMCacheManager) are included in spring-cache-4.1.3.jar (com.google.code.simple-spring-memcached:spring-cache). This jar is a part of SSM project, it is not a part of Spring project.

Features

Custom expiration time

One expiration time for all elements in cache may be insufficient when one cache stores objects of different domain types. In such case the best solution is defining expiration per annotation but Spring Cache doesn't support it out of the box. ExtendedSSMCacheManager overcomes this limitation and allow to pass expiration time as a part of cache name. To define custom expiration on method as a cache name use concatenation of specific cache name, separator (by default #) and expiration e.g.:

<?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:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
  	   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.3.xsd
           http://www.springframework.org/schema/cache 
           http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">

  <cache:annotation-driven />
   
  <bean name="cacheManager" class="com.google.code.ssm.spring.ExtendedSSMCacheManager">
    <property name="caches">
      <set>
        <bean class="com.google.code.ssm.spring.SSMCache">
	  <constructor-arg name="cache" index="0" ref="defaultCache" />
          <!-- 5 minutes -->
	  <constructor-arg name="expiration" index="1" value="300" />
          <!-- @CacheEvict(..., "allEntries" = true) won't work because allowClear is false, 
          so we won't flush accidentally all entries from memcached instance -->
	  <constructor-arg name="allowClear" index="2" value="false" />
	</bean>
      </set>
    </property>
  </bean>

  <bean name="defaultCache" class="com.google.code.ssm.CacheFactory">
    <property name="cacheName" value="default" />
    <property name="cacheClientFactory">
      <bean name="cacheClientFactory" class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" />
    </property>
    <property name="addressProvider">
      <bean class="com.google.code.ssm.config.DefaultAddressProvider">
        <property name="address" value="127.0.0.1:11211" />
      </bean>
    </property>
    <property name="configuration">
      <bean class="com.google.code.ssm.providers.CacheConfiguration">
        <property name="consistentHashing" value="true" />
      </bean>
    </property>
  </bean>
</beans>
// cache name: default, expiration: 600s instead of default 300s
@Cacheable(value = "default#600", key = "#id")
public User getByPk(int id) {
  // ...
  return result;
}

Alternative cache storage

Simple Spring Memcached despite its name can store cached data not only in memcached. Data may be stored in all cache providers that support memcached protocol (the textual or binary). All those caches can be used also with Spring Cache (through SSM).

Infinispan

Infinispan supports memcached text protocol, so it can be used with SSM instead of (or together with) memcached server. Here you can find more information.

Couchbase

According to documentation Couchbase supports the textual memcached protocol.

Amazon (AWS) ElastiCache

Amazon allow to create Cache Clusters, each comprised of one or more Cache Nodes. Each Cache Cluster is a distributed, in-memory cache that can be accessed using the Memcached protocol. You can use SSM with any of available providers to connect to ElastiCache, just provide address to your instance in CacheFactory e.g.:

 <bean class="com.google.code.ssm.config.DefaultAddressProvider">
     <property name="address" value="xxxxx.yyyyy.cache.amazonaws.com:11211"/>
 </bean>

If you don't want to provide static address of memcached instances but instead use auto discovery feature then choose aws-elsticache-provider and in your CacheFactory provide address to configuration endpoint (addressProvider):

 <bean class="com.google.code.ssm.config.DefaultAddressProvider">
     <!-- set only single address to configuration endpoint -->
     <property name="address" value="xxxxx.yyyyy.cfg.zzzz.cache.amazonaws.com:11211"/>
 </bean>

and set mode to dynamic (configuration):

 <bean class="com.google.code.ssm.providers.elasticache.ElastiCacheConfiguration">
     <!-- use dynamic configuration and fetch configuration of nodes from cluster (auto discovery node feature is enabled) -->
     <property name="clientMode" value="#{T(net.spy.memcached.ClientMode).Dynamic}" />
 </bean>

In this mode, the set of cache node endpoints and any updates to it is dynamically managed. The client is initialized with a configuration endpoint and will periodically learn about the cache nodes in the cluster.

Keep in mind that due to Amazon security policy it's not possible to connect to Amazon Cache Cluster (memcached) from local/developer machine. To make it work an application has to be deployed on Amazon.

Additional Information

Exceptions

Great care was taken while writing SSM to preserve the functionality of any surrounding code. If an exception is thrown anywhere in the SSM code, we've gone to great lengths to catch and log (slf4j) as much information as possible from the exception, but we refrain from passing that exception up to the end user.

Serialization

Any of the objects being saved to the cache should implement java.io.Serializable in the very least, but it is highly recommended that you implement java.io.Externalizable in as many classes of the cache data as is possible. Java serialization used by providers (spymemcached or xmemcached) is a default mechanism.
There are four available serialization types (com.google.code.ssm.api.format.SerializationType):

  • PROVIDER: the default one, delegate serialization of cached object to memcached client (spymemcached or xmemcached)
  • JSON: through amazing jackson library, as a part of serialized string object type is stored
  • JAVA: simple java serialization
  • CUSTOM: uses custom com.google.code.ssm.providers.CacheTranscoder

Default serialization type

Default seriaization type for each cache is set by com.google.code.ssm.CacheFactory#setDefaultSerializationType. Using SerializationType.CUSTOM as default serialization requires also setting custom transcoder by com.google.code.ssm.CacheFactory#setCustomTranscoder.

Different serialization type per method

Serialization type can be also specified per cached method using @com.google.code.ssm.api.format.Serialization annotation. This annotation overwrite default serialization type set in CacheFactory.

Order of cache advice

Since 3.2.0 SSM cache advice runs before transaction advice. The default SSM cache advice order value is 0. The order can be set in the application context file to any custom value:

<bean class="com.google.code.ssm.Settings">
  <property name="order" value="500" />
</bean>

Contact Us

If you have any questions, feel free to ask them on the Google Group. (UPDATE: Sorry, this link was bad up until 02 Aug '09, because I fat-fingered when creating the Google Group. I incorrectly misspelled it as 'simple-spring-memEcached'. So sorry about that!)

Also, let us know if you are using SSM in your project, and we will list it in on the Wiki.