The docs say:
Self-Invocation
Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with @Cacheable.
BTW, I had to tweak your test project to satisfy sbt.
Here are the two stack traces (below specs2) for comparison.
Externally intercepted:
at com.github.fommil.cache.Thingy.expensive(Thingy.scala:20)
at com.github.fommil.cache.Thingy$$FastClassByCGLIB$$f58b1afb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at com.googlecode.ehcache.annotations.resolver.ThreadLocalCacheEntryFactory.createEntry(ThreadLocalCacheEntryFactory.java:36)
at net.sf.ehcache.constructs.blocking.SelfPopulatingCache.get(SelfPopulatingCache.java:73)
at net.sf.ehcache.constructs.blocking.BlockingCache.get(BlockingCache.java:243)
at com.googlecode.ehcache.annotations.interceptor.EhCacheInterceptor.invokeSelfPopulatingCacheable(EhCacheInterceptor.java:174)
at com.googlecode.ehcache.annotations.interceptor.EhCacheInterceptor.invokeCacheable(EhCacheInterceptor.java:122)
at com.googlecode.ehcache.annotations.interceptor.EhCacheInterceptor.invoke(EhCacheInterceptor.java:81)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.github.fommil.cache.Thingy$$EnhancerByCGLIB$$d7d992e.expensive(<generated>)
at com.github.fommil.cache.Thingy$$FastClassByCGLIB$$f58b1afb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:132)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.github.fommil.cache.Thingy$$EnhancerByCGLIB$$c2c26afd.expensive(<generated>)
at com.github.fommil.cache.ThingySpec$$anonfun$1$$anonfun$apply$5.apply(ThingySpec.scala:16)
at com.github.fommil.cache.ThingySpec$$anonfun$1$$anonfun$apply$5.apply(ThingySpec.scala:15)
...and internally invoked:
at com.github.fommil.cache.Thingy.expensive(Thingy.scala:20)
at com.github.fommil.cache.Thingy.internallyCalling(Thingy.scala:24)
at com.github.fommil.cache.Thingy$$FastClassByCGLIB$$f58b1afb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:627)
at com.github.fommil.cache.Thingy$$EnhancerByCGLIB$$d7d992e.internallyCalling(<generated>)
at com.github.fommil.cache.Thingy$$FastClassByCGLIB$$f58b1afb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:132)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.github.fommil.cache.Thingy$$EnhancerByCGLIB$$c2c26afd.internallyCalling(<generated>)
at com.github.fommil.cache.ThingySpec$$anonfun$1$$anonfun$apply$6.apply(ThingySpec.scala:22)
at com.github.fommil.cache.ThingySpec$$anonfun$1$$anonfun$apply$6.apply(ThingySpec.scala:21)
Not surprisingly, something like the following works:
import org.springframework.beans.factory.{ BeanFactory, BeanFactoryAware }
@Service
@Scope(proxyMode = TARGET_CLASS)
class Thingy extends JavaLogging with BeanFactoryAware {
var called = 0
@Cacheable(cacheName = "thingy", decoratedCacheType = SELF_POPULATING_CACHE)
def expensive() = {
called += 1
Thread.sleep(1000)
new Throwable().printStackTrace()
"bob"
}
var myProxy: Thingy = _
var myFactory: BeanFactory = _
def setBeanFactory(beanFactory: BeanFactory): Unit = {
myFactory = beanFactory
}
def internallyCalling() = {
if (myProxy == null) myProxy = myFactory getBean classOf[Thingy]
myProxy.expensive()
}
}
Just to make sure there was no local instrumentation going on, which was maybe disabled depending on the call stack, I tried it by calling out to the companion object and back, and also wrapping it in a future, but the magic is at the proxy.