我有一种方法可以像这样调用另一个@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()中的技巧吗?
有没有人遇到类似的问题?
谢谢!
答案 0 :(得分:6)
没有JVM技巧,即findMethod1()
内部findMethod2()
或其他任何内容都没有内联。
问题是你的代码绕过了"代理" Spring 围绕您的应用程序类(包含findMethod1()
)创建@Cacheable
注释。
与 Spring的 事务性注释和底层基础架构一样,给定一个接口,默认情况下 Spring 将创建一个JDK动态代理(AOP样式)到#34 ;截距"方法调用并应用"建议" (由注释类型决定,在本例中为缓存)。但是,一旦从代表目标对象的拦截器(代理)调用目标对象以应用建议,则线程现在在目标对象的上下文中执行,因此目标对象内的任何后续方法调用都在发生直接在目标对象上。
看起来有点像......
caller -> Proxy -> findMethod2() -> findMethod1()
理想情况下,你想要的是......
caller -> Proxy -> findMethod2() -> Proxy -> findMethod1()
但是,Thread已经在" target"的上下文中执行了。对象一旦进入findMethod2()
,所以你最终会得到第一个调用堆栈。
Spring文档更好地解释了here。
该文档接着指出了这个问题的解决方案,最有利的是重构代码以确保调用者通过代理拦截器进行第二次方法调用(即findMethod1()
)。
我还收集另一个解决这个问题的方法是使用完整的AspectJ
,在应用程序构建过程中使用编译器和字节码编织器来修改实际的目标对象,以便后续的调用来自目标对象拦截并相应地应用建议。
请参阅Spring AOP
与完整AspectJ
之间的trade-offs上的Spring文档,以及Spring应用程序中的how to use full AspectJ。
希望这有帮助。
干杯!
答案 1 :(得分:0)
我觉得很方便的其他解决方案是使用@Resource,然后使用该资源引用(在您的情况下为method1)和https://stackoverflow.com/a/48867068/2488286来调用目标