Element-Level Caching of Collection Mapping Methods

Annotation Based Collection Caching

If you have ever used theehcache-spring-annotations packageSpring caching abstraction then you know what an awesome thing method-level caching is. If you haven't used it, go check it out now! To summarise (poorly), you annotate a method with "@Cacheable" and the package uses proxying to wrap the method invocation in a cache template.

It's more complicated, obviously, but the benefits and easy of this pattern are obvious. In addition, you can leverage all the power and flexibility of ehcache to do your actual caching. I apply this to all my MVC service implementations for instant web-service caching.

Limitations

One issue I had, though, was when working with collection-to-collection mapping methods. This is a common pattern (for me, anyway) where a list of type A is converted to a list of type B in an idempotent stateless manner.

If you annotate such a method as @Cacheable it will only cache complete result mappings, which can still be useful if you need to map {A,B,C} -> {X,Y,Z} on a regular basis. What would really be neat, though, is if caching were applied to each element individually, with only the unknown values being passed on for resolution.

Enter the Aspect

This is a perfect application of AOP (aspect-oriented programming). Although I'm no AOP expert, I was able to get my feet wet and enable just such a solution in only about two hours, thanks to the excellent documentation provided with the Spring. This is a pretty bare-bones implementation, but it illustrates the important AOP bits.

Annotation

First we must declare a new custom annotation with which we will mark any method that meets are requirements. By using explicit annotation-based configuration we give responsibility over the proper use of this aspect to the programmer. In this configuration, we allow unordered Collection:Collection mapping by specifying which field in the result objects contains the request key. An ordered List:List mapping is also possible by specifying "IMPLICIT" as the keyField.

Advice Class

Next we create the actual advice class. We use @Aspect annotation to make it an aspect, and add a setter to allow injection of a Spring CacheManager object. We also have the key generator and convenience methods for casting.

Unordered Operation

Unordered mapping is the simpler of the two multi-value modes of operation, since we need not worry about maintaining the order of the request since the cache key is explicitly found in the result values.

Conclusions

This, of course, is just part of a full solution. It should be easy to add additional annotations and functionality to allow adding of individual items to the same cache, and for triggering removal of elements either via a list or individually. It's a long way from being as feature-filled or rigorous as the original ehcache-spring-annotations package but solves a specific problem and is a good introduction to AOP in Spring.