我有一个方法可以像这样调用另一个@Cacheable 方法:
public ItemDO findMethod2(long itemId) {
this.findMethod1(itemId);
...
}
@Cacheable(value = "Item", key="#itemId", unless="#result == null")
public ItemDO findMethod1(long itemId) {
...
}
如果我直接调用 findMethod1(),缓存会很好地工作。但是,当我调用 findMethod2() 时,findMethod1() 上的缓存被完全忽略。
会不会是 JVM 搞的把 findMethod1() 内联到 findMethod2()?
有没有人遇到过类似的问题?
谢谢!
最佳答案
这不是 JVM 技巧,即 findMethod1()
未内联 findMethod2()
或任何类似性质的东西。
问题是您的代码绕过了 Spring 围绕您的应用程序类(包含 findMethod1()
)为 @Cacheable
创建的“代理”注释。
像 Spring 的事务注解和底层基础设施,给定一个接口(interface),默认 Spring 会创建一个 JDK 动态代理(AOP 风格)来“拦截”方法调用和应用“建议”(由注释类型决定,在本例中为缓存)。但是,一旦从代表目标对象的拦截器(代理)调用目标对象以应用建议,Thread 现在就在目标对象的上下文中执行,因此从目标对象内部进行的任何后续方法调用都会发生直接在目标对象本身上。
看起来有点像这样......
caller -> Proxy -> findMethod2() -> findMethod1()
理想情况下你想要的是这个......
caller -> Proxy -> findMethod2() -> Proxy -> findMethod1()
但是,线程已经在“目标”对象的上下文中执行一次 findMethod2()
, 所以你最终得到了第一个调用堆栈。
Spring 文档对其进行了更好的解释 here .
文档继续指出了这个问题的解决方案,最有利的是重构您的代码以确保调用者正在通过代理拦截器进行第二次方法调用(即 findMethod1()
)。
我还收集到另一个解决这个问题的方法是使用成熟的 AspectJ
,在您的应用程序构建过程中使用编译器和字节码编织器来修改实际的目标对象,以便从目标对象内部进行的后续调用拦截并相应地应用建议。
请参阅 trade-offs 上的 Spring 文档在 Spring AOP
之间和完整的AspectJ
,以及 how to use full AspectJ在您的 Spring 应用程序中。
希望这对您有所帮助。
干杯!
关于spring-cache - spring 缓存确实可以使用嵌套方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32207964/