We successfully use the Caching SpringModule. In short, this allows us to declaratively cache values returned from method calls. The Caching module supports many different cache implementations; we have picked OsCache for development. To get started, let’s take a look at the MemberService and its implementation.
public interface MemberService {
@Nullable
Member complicatedLookup(@NotNull ComplexArgument argument);
}
public class DefaultMemberService implements MemberService {
@Nullable
@Cacheable(modelId = "Member")
public Member complicatedLookup(@NotNull ComplexArgument argument) {
// some complex stuff
}
}
This code works perfectly well: when you call the DefaultMemberService.complicatedLookup(...) method more than once with the same (read equal(Object)) ComplexArgument object, the caching framework intercepts the call in and only the first call will go to do the // some complex stuff.
The problem is when you return an object from a Hibernate DAO call and that object is enhanced by CGLIB. Now the cache has a reference to an object that references an invalid Session.
We found this when our integration tests worked fine on their own, but started failing when we ran them as part of our automatic build. The prospect of going through every possible code path to find out if there is a cached CGLIB-enhanced object was daunting. Luckily, Spring AOP was quick to rescue. We wrote a simple little aspect that checks that any method with the @Cacheable annotation does not return a CGLIB-enahanced object.
@Aspect
public class CacheManagerAspect {
@AfterReturning(pointcut =
"@annotation(org.springmodules.cache.annotations.Cacheable)",
returning = "ret",
argNames = "jp, ret")
public void indicateLazilyLoaded(JoinPoint jp, Object ret) throws Exception {
checkNotCGLIBEnhanced(jp, ret);
}
private void checkNotCGLIBEnhanced(JoinPoint jp, Object o)
throws IllegalAccessException, InvocationTargetException {
if (o == null) return;
String name = o.getClass().getName();
if (!name.startsWith("uk.co.package")) return;
if (name.contains("$$Enhancer"))
throw new RuntimeException(
"You cannot cache a CGLIB-Enhanced object at " + jp);
PropertyDescriptor[] descriptors =
BeanUtils.getPropertyDescriptors(o.getClass());
for (PropertyDescriptor descriptor : descriptors) {
Object value = descriptor.getReadMethod().invoke(o);
checkNotCGLIBEnhanced(jp, value);
}
}
}
Simple, but powerful stuff from SpringModules and Spring AOP!
Websites
Subscribe to our RSS Feed
Search
-
Recent Posts
Archive
Categories
agile AJAX Akka Business Cake pattern Cloud dm Server Functional programming Haskell intellij intellij idea java Java EE Javascript jQuery lift Monads mongodb Open Spring 3 OSGi Play play 2.0 Programmers' humour Projects racing ruby sbt sbteclipse Scala Scalad scala ide Scalaz shapeless Specs2 Specs2 Spring Specs2 Spring Web spray Spring Spring Data Spring Framework spring mvc Spring User Group Talk training typesafe
