【问题标题】:spring cache does work w/ nested method弹簧缓存确实可以使用嵌套方法
【发布时间】:2015-08-25 15:22:44
【问题描述】:

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

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

谢谢!

【问题讨论】:

    标签: spring-cache


    【解决方案1】:

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

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

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

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

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

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

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

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

    Spring 文档解释得更好here

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

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

    请参阅 Spring AOP 和完整 AspectJ 之间的 trade-offs 上的 Spring 文档,以及您的 Spring 应用程序中的 how to use full AspectJ

    希望这会有所帮助。

    干杯!

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 2021-12-09
        • 1970-01-01
        • 1970-01-01
        • 2012-07-07
        • 2016-09-11
        • 2020-12-20
        • 1970-01-01
        • 1970-01-01
        • 2014-03-03
        相关资源
        最近更新 更多