我在scala-spring-cache中建立了一个小的测试项目,以演示在scala中使用ehcache-spring注释。
为了使缓存发挥作用,必须使用方面。我有使用CGLIB或AspectJ的配置(或者可以添加@Scope(proxyMode = TARGET_CLASS)
来获取该bean的代理)。
对于自动代理方面,当从类外部调用时,缓存工作正常。然而,当方法在类内部调用时,它会失败:
@Cacheable(cacheName = "thingy", decoratedCacheType = SELF_POPULATING_CACHE)
def expensive() = "bob"
def internallyCalling() = expensive()
这里,类外部对expensive()
的调用将使用缓存,但对internallyCalling()
的调用将不使用缓存。
如何修复此问题,以便缓存可以在内部使用(如Java中)这可能吗?
文档说:
自调用
只有通过代理传入的外部方法调用拦截。这意味着自调用实际上是一种方法在目标对象内调用目标对象的另一方法,即使被调用的方法标记为@Cacheable。
顺便说一句,我不得不调整你的测试项目来满足sbt。
下面是两个堆栈跟踪(在规范2下面),用于比较。
外部截获:
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)
内部调用:
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)
毫不奇怪,类似以下工作:
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()
}
}
为了确保没有本地插入,这可能会根据调用堆栈而被禁用,我通过调用伴随对象并返回进行了尝试,并在将来对其进行包装,但神奇之处在于代理。
这在Scala中似乎是不可能的。