spring 缓存确实可以使用嵌套方法

spring cache does work w/ nested method

我有一个方法可以像这样调用另一个@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 围绕您的应用程序 class 创建的 "Proxy"(包含 findMethod1()) 对于 @Cacheable 注释。

喜欢Spring的事务注解和底层基础设施,给定接口,默认Spring将创建一个 JDK 动态代理(AOP 样式)到 "intercept" 方法调用并应用 "advice"(由注释类型决定,在本例中为缓存)。但是,一旦从代表目标对象的拦截器(代理)调用目标对象以应用建议,Thread 现在就在目标对象的上下文中执行,因此从目标对象内部进行的任何后续方法调用都会发生直接在目标对象本身上。

看起来有点像这样...

caller -> Proxy -> findMethod2() -> findMethod1()

理想情况下你想要的是这个...

caller -> Proxy -> findMethod2() -> Proxy -> findMethod1()

但是,Thread 已经在 "target" 对象的上下文中执行一次 findMethod2(),因此您最终得到第一个调用堆栈。

Spring 文档解释得更好 here

文档继续指出了这个问题的解决方案,最有利的是重构你的代码以确保调用者正在通过代理拦截器进行第二次方法调用(即 findMethod1())。

我还收集到另一个解决这个问题的方法是使用成熟的 AspectJ,在你的应用程序构建过程中使用编译器和字节码编织器来修改实际的目标对象,以便后续调用在目标对象中拦截并相应地应用建议。

请参阅 Spring 应用程序 trade-offs between Spring AOP and full AspectJ, as well as how to use full AspectJ 上的 Spring 文档。

希望这对您有所帮助。

干杯!

我觉得方便的其他解决方案是使用@Resource,然后使用该资源引用和

调用目标(在您的情况下为 method1)