2

我有一种方法可以调用另一个 @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()中的伎俩?

有没有人遇到过类似的问题?

谢谢!

4

2 回答 2

11

这不是 JVM 技巧,即findMethod1()没有被内联findMethod2()或任何类似性质的东西。

问题是您的代码绕过了Spring围绕您的应用程序类(包含findMethod1())为@Cacheable注释创建的“代理”。

Spring 的事务注解和底层基础设施一样,给定一个接口,默认情况下Spring将创建一个 JDK 动态代理(AOP 样式)来“拦截”方法调用并应用“建议”(由注解的类型决定,在这种情况下,缓存)。但是,一旦从代表目标对象的拦截器(代理)调用目标对象以应用建议,线程现在正在目标对象的上下文中执行,因此来自目标对象内的任何后续方法调用都会发生直接作用于目标对象本身。

它看起来有点像这样......

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

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

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

但是,线程已经在“目标”对象的上下文中执行findMethod2(),因此您最终会得到第一个调用堆栈。

Spring 文档在这里更好地解释了它。

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

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

请参阅 Spring 文档,了解与 full之间的权衡,以及如何在 Spring 应用程序中使用 full AspectJ。Spring AOPAspectJ

希望这可以帮助。

干杯!

于 2015-08-26T15:37:41.533 回答
0

我觉得方便的其他解决方案是使用@Resource,然后使用带有https://stackoverflow.com/a/48867068/2488286的资源引用调用目标(在您的情况下为method1)

于 2019-07-23T06:30:41.207 回答