Thursday, September 20, 2012

Proxy vs AspectJ Caching with Spring

All my previous caching examples used Proxy caching.  
<cache:annotation-driven/> has a 'mode' attribute which by default is set to 'proxy'. Spring will proxy your annotated beans using Spring's AOP framework. This means that cross cutting will be applied to method calls coming in through the proxy. How can that potentially be a problem?

Let's take a simple example of calling a service to retrieve a Book.
  
Book has a references to an Author object
We will cache Book and Author objects separately (to avoid duplication) and when the service returns an instance of the Book, Author will be set. We annotated getBookWithNoAuthor and getAuthor methods, which means the result of those methods will be cached by ISBN number.

When calling libraryService.getBook(isbn), I noticed that caching is not used, however when calling libraryService.getBookWithNoAuthor(isbn), caching is triggered.

Explanation: Spring created a Proxy libraryService class and all caching invocation is done inside the proxy. Once the proxy calls your libraryService, any other method invocations (this.getBookWithNoAuthore, this.getAuthor) from within the class are not routing through the proxy.

Solution: Other than merging all 3 methods and potentially caching duplicate data, our other solution is to use AspectJ weaving.

You can read more on AspectJ and how it works, but in summary it will modify the target class byte code to include caching for each method, there is no proxy creation.

AspectJ Setup: 
  1. In your Spring context file, add mode="aspectj"  
    <cache:annotation-driven mode="aspectj"/>
  2. In your Spring context file, add <context:load-time-weaver/>
  3. Add dependencies to your pom file:

For JUnit testing make sure you add a JVM variable:
     -javaagent:\spring-instrument.jar

For Tomcat you need to specify class loader explicitly.

  1.  Copy org.springframework.instrument.tomcat.jar to $CATALINA_HOME/lib. 
  2.  Update server.xml to instruct Tomcat to use a custom class loader: 
That's it! If you can get away with using default mode (proxy) then stick with that.

2 comments:

  1. Have you used spring caching in aspect mode with compile time weaving(CTW)... if yes can you share the configuration file? I am facing some issue where the caching is not happening in CTW

    ReplyDelete
  2. Sorry for late response. I don't have any examples at the moment with aspectJ and compile time weaving. Any reason why you decided not to go with load-time ?

    ReplyDelete