@transactional @Asyncおよびその他の注釈は機能しません
以前は、春に@Transactional、@Async、その他の注釈を使用するときに注釈が機能しない状況に多くの人が遭遇しました。
なぜこれらの状況が起こるのですか?これらの注釈の関数は実際にSpring AOPによって実装されており、その実装原則はプロキシを通じて実装されているためです。
JDKダイナミックプロキシ
JDKダイナミックプロキシの基本原則を簡単に理解しましょう。
//ターゲットクラスインターフェイスパブリックインターフェイスjdkproxytestservice {void run();} //ターゲットクラスパブリッククラスjdkproxytestserviceimplを実装しますjdkproxytestservice {public void run(){system.out.println( "do home ..."); }} //プロキシクラスパブリッククラスtestjdkproxyを実装するrivocationhandler {private object targetobject; //プロキシターゲットオブジェクト//プロキシオブジェクトパブリックオブジェクトNewProxy(Object TargetObject){this.targetObject =ターゲットオブジェクト; proxy.newProxyInstance(targetObject.getClass()。getClassLoader()、TargetObject.getClass()。getInterfaces()、this); } //リフレクションを使用して、元のロジックで論理強化を実行するパブリックオブジェクトInvoke(Object Proxy、Method Method、Object [] Args)スロー可能{//トランザクションのシミュレーションSIMIGINTRANSACTION(); //元の実行ロジックオブジェクトret = method.invoke(targetobject、args); // mock Transaction Submission AssionCommitTransaction();返品; } private void sasumeBegintransaction(){system.out.println( "mock transaction start ..."); } private void assomecommittransaction(){system.out.println( "mock transaction submission ..."); }} // public class test {public static void main(string [] args){testjdkproxy jdkproxy = new testjdkproxy(); jdkproxytestService proxy =(jdkproxytestservice)jdkproxy.newproxy(new jdkproxytestserviceimpl()); proxy.run(); }}上記の例は、JDKダイナミックプロキシの原則を明確に説明できるはずです。反射メカニズムを使用して、プロキシインターフェイスを実装する匿名クラスを生成し、特定のメソッドを呼び出す前にInvokeHandlerを処理します。プロキシクラスオブジェクトを介してメソッドを呼び出すと、実際にそのInvokeメソッドを最初に呼び出してから、元のメソッドを呼び出します。このようにして、元のメソッドロジックの前後に処理ロジックを均一に追加できます。
Springには、cglib動的プロキシである動的プロキシメソッドもあります。プロキシオブジェクトクラスのクラスファイルをロードし、バイトコードを変更してサブクラスを生成することにより処理します。処理方法は異なりますが、代理店のアイデアは一貫しています。
プロキシになっているターゲットオブジェクトがインターフェイスを実装する場合、SpringはデフォルトでJDKダイナミックプロキシを使用します。このターゲットタイプによって実装されたすべてのインターフェイスはプロキシになります。ターゲットオブジェクトがインターフェイスを実装していない場合、CGLIBプロキシが作成されます。
スプリングAOP注釈の失敗と解決
動的プロキシ原理の上記の分析に基づいて、次の2つの一般的な問題を見てみましょう。
同じクラスでは、メソッドAはメソッドB(方法Bに注釈が付けられています)を呼び出し、注釈は無効です。
すべてのスプリングAOP注釈について、春が豆をスキャンするときにそのような注釈が見つかった場合、プロキシオブジェクトを動的に構築します。
このアノテーションは、クラスXのオブジェクトを介して注釈を付けてAメソッドを直接呼び出す場合に有効です。この時点で、Springは呼び出そうとしているメソッドにAOPアノテーションがあると判断し、クラスXのプロキシオブジェクトを使用してメソッドAを呼び出すために使用します。
ただし、クラスXのメソッドAが注釈を使用してメソッドBを呼び出し、クラスXのスルーオブジェクトAを呼び出すと、メソッドBの注釈が無効であると仮定します。 Springは、A Callが注釈がないと判断するため、プロキシオブジェクトではなく元のオブジェクトはまだ使用されています。次にbを呼び出すと、元のオブジェクトのメソッドBの注釈はもちろん無効です。
解決:
最も簡単な方法は、もちろんメソッドAとBに依存関係がなく、クラスXのオブジェクトを介してメソッドBを直接呼び出すことができることです。
しかし、多くの場合、私たちのロジックはこのようによく書かれていないかもしれないので、別の方法があります。プロキシオブジェクトを手動で取得する方法を見つけることです。
AOPContextクラスには、現在のクラスのプロキシオブジェクトを直接取得できるCurrentProxy()メソッドがあります。次に、上記の例を次のように解決できます。
//メソッドB内のメソッドbを呼び出す//1。bを直接呼び出すと、注釈は無効です。 b()// 2。プロキシクラスオブジェクトを取得し、Bを呼び出します。((x)aopcontext.currentproxy())。
@AutowiredオブジェクトはAOPアノテーション方法でnullです
以前の使用では、注釈法では、他の注入されたオブジェクトを使用する場合、オブジェクトが注入されておらず、nullであることがわかりました。
最後に、この理由は、この方法がプライベートだったためです。 SpringはJDK Dynamic ProxyまたはCGLib Dynamic Proxyを使用するため、1つはインターフェイスを実装するクラスで、もう1つはサブクラスを介して実装されます。インターフェイスも親クラスもプライベートメソッドを存在させることはできません。そうしないと、サブクラスも実装クラスもオーバーライドできません。
方法がプライベートな場合、この方法はプロキシプロセスでは見られず、プロキシオブジェクトの作成に問題を引き起こし、いくつかのオブジェクトを注入しません。
そのため、メソッドがAOPアノテーションを使用する必要がある場合は、非プライベートメソッドに設定します。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。