All my previous caching examples used Proxy caching.
Let's take a simple example of calling a service to retrieve a Book.
Book has a references to an Author object
<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.
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:
- In your Spring context file, add mode="aspectj"
<cache:annotation-driven mode="aspectj"/> - In your Spring context file, add <context:load-time-weaver/>
- 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.
- Copy org.springframework.instrument.tomcat.jar to $CATALINA_HOME/lib.
- Update server.xml to instruct Tomcat to use a custom class loader: