2

次のような別の @Cacheable メソッドを呼び出すメソッドが 1 つあります。

public ItemDO findMethod2(long itemId) {
    this.findMethod1(itemId);
    ...
}

@Cacheable(value = "Item", key="#itemId", unless="#result == null")
public ItemDO findMethod1(long itemId) {
    ...
}

findMethod1() を直接呼び出すと、キャッシュはうまく機能します。ただし、findMethod2() を呼び出すと、findMethod1() のキャッシュは完全に無視されます。

findMethod1() を findMethod2() にインライン化するのは、JVM によって行われたトリックでしょうか?

誰も同様の問題に遭遇しますか?

ありがとう!

4

2 に答える 2

11

これは JVM のトリックでfindMethod1()はありません。つまり、内部でインライン化されfindMethod2()たり、そのような性質のものではありません。

問題は、 Springが注釈用のアプリケーション クラス (を含むfindMethod1()) の周りに作成する「プロキシ」をコードがバイパスしていることです。@Cacheable

Spring のTransactional アノテーションと基盤となるインフラストラクチャのように、インターフェースが与えられると、 Springはデフォルトで JDK Dynamic Proxy (AOP スタイル) を作成して、メソッド呼び出しを「インターセプト」し、「アドバイス」を適用します (この場合、アノテーションのタイプによって決定されます)。 、キャッシング)。ただし、ターゲット オブジェクトに代わってアドバイスを適用するインターセプター (プロキシ) からターゲット オブジェクトが呼び出されると、スレッドはターゲット オブジェクトのコンテキストで実行されるようになり、ターゲット オブジェクト内からの後続のメソッド呼び出しが発生します。ターゲット オブジェクト自体に直接。

ちょっとこんな感じ・・・。

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

理想的には、これが必要です...

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

ただし、 Thread は、内部で「ターゲット」オブジェクトのコンテキストで既に実行されているfindMethod2()ため、最初の呼び出しスタックになります。

Spring doc は、ここでそれをよりよく説明しています。

このドキュメントでは、この問題の解決策を指摘しています。最も望ましいのは、コードをリファクタリングして、呼び出し元が 2 番目のメソッド呼び出し (つまりfindMethod1()) でプロキシ インターセプターを通過するようにすることです。

この問題に対する別の解決策としてAspectJ、アプリケーションのビルド プロセス中にコンパイラとバイトコード ウィーバーを使用して実際のターゲット オブジェクトを変更し、ターゲット オブジェクト内からの後続の呼び出しがインターセプトされ、それに応じてアドバイスが適用されるようにすることも考えられます。 .

と fullの間のトレードオフ、およびSpring アプリケーションで完全な AspectJ を使用する方法については、Spring のドキュメントを参照してください。Spring AOPAspectJ

お役に立てれば。

乾杯!

于 2015-08-26T15:37:41.533 に答える