Javaで動的プロキシを実装するための鍵は、これらの2つのことです。プロキシとInvocationHandlerです。 InvocationHandlerインターフェイスのInvokeメソッドから始めて、Javaが動的プロキシをどのように実装するかを簡単に説明しましょう。
まず、Invokeメソッドの完全な形式は次のとおりです。
パブリックオブジェクトの呼び出し(オブジェクトプロキシ、メソッドメソッド、オブジェクト[] args)スロースロー可能{method.invoke(obj、args); return null;}まず、その方法が呼ばれるメソッド、つまり実行する必要があるメソッドであると推測しましょう。 ARGSはメソッドのパラメーターです。プロキシ、このパラメーターは何ですか? Invoke()メソッドの上記の実装は、比較的標準的なフォームです。ここでは、プロキシパラメーターが使用されていないことがわかります。次のように、JDKドキュメントのプロキシの説明をご覧ください。
プロキシインターフェイスの1つを介したプロキシインスタンスでのメソッド呼び出しは、インスタンスの呼び出しハンドラーの呼び出しメソッドに発送され、プロキシインスタンス、java.lang.refllect.methodオブジェクトが呼び出されたメソッド、および引数を含むタイプオブジェクトの配列を識別します。
このことから、上記の推測が正しいことを知ることができ、プロキシパラメーターがプロキシクラスのインスタンスであることも知っています。
説明の便利さのために、動的プロキシを実装する簡単な例を次に示します。
//抽象ロール(動的プロキシはプロキシインターフェイスのみ)パブリックインターフェイス件名{public void request(); } //実質的な役割:件名のリクエスト()パブリッククラスのメソッドRealSubjectを実装した件名{public void request(){system.out.println( "from real subject。"); }} // InvocationHandler Public Class DynamicSubjectを実装してくださいInvocationHandler {private object obj; //これは動的プロキシの利点です。カプセル化されたオブジェクトはオブジェクトタイプであり、あらゆるタイプのpublic dynamicsubject(){} public dynamicsubject(object obj){this.obj = obj; } //このメソッドは、パブリックオブジェクトを呼び出すために示すものではありません(オブジェクトプロキシ、メソッドメソッド、オブジェクト[] arg)スロー可能{system.out.println( "calling" + method); method.invoke(obj、args); system.out.println( "" + methodを呼び出した後); nullを返します。 }} //クライアント:プロキシインスタンスを生成し、リクエスト()メソッドパブリッククラスクライアントを呼び出します{public static void main(string [] arg)スロー可能{// todo auto-fenerated method stub rs = new realsubject(); //プロキシクラスの請求書(rs)= new dynamicsubject(rs); class <?> cls = rs.getClass(); //以下は、1回限りのプロキシ件名=(件名)proxy.newproxyinstance(cls.getClassLoader()、cls.getInterfaces()、ds)です。 //ここで、結果を実行することにより、主題がプロキシのインスタンスであることを証明できます。このインスタンスは、サブジェクトインターフェイスsystem.out.println(件名Instanceofプロキシ)を実装しています。 //ここでは、被験者のクラスクラスが$ proxy0であることがわかります。この$ proxy0クラスはプロキシを継承し、件名インターフェイスsystem.out.println( "件名のクラスクラスは次のとおりです。 System.out.print( "主題のプロパティは次のとおりです。 field [] field = subject.getClass()。getDeclaredFields(); for(field f:field){system.out.print(f.getname()+"、"); } system.out.print( "/n"+"件名のメソッドは:"); method [] method = subject.getClass()。getDeclaredMethods(); for(メソッドM:メソッド){System.out.print(m.getName()+"、"); } system.out.println( "/n"+"件名の親クラスは次のとおりです。 System.out.print( "/n"+"サブジェクトがインターフェイスを実装します:"); class <? for(class <? } system.out.println( "/n/n"+"run result is:"); subject.request(); }}操作の結果は次のとおりです。パッケージ名はここで省略されています***代わりに
真実
件名のクラスクラスは次のとおりです。クラス$ proxy0
主題の特性は、M1、M3、M0、M2、
主題の方法は次のとおりです。リクエスト、ハッシュコード、等しい、トストリング、
主題の親クラスは次のとおりです。クラスjava.lang.reflt.proxy
件名によって実装されるインターフェイスは、cn.edu.ustc.dynamicproxy.subject、です。
操作結果は次のとおりです。
パブリックアブストラクトvoidを呼び出す前に***。subject.request()
本当の主題から。
パブリックアブストラクトボイドを呼び出した後***。subject.request()
PS:少なくとも私にとっては、この結果に関する情報は非常に重要です。ダイナミックプロキシにおける私のめまいの根本的な原因は、上記の件名を誤解したことであるため、少なくとも表面に混乱し、主題とプロキシの間のつながりが見つかりませんでした。私はかつて、最後のcall request()がinvoke()に接続された方法と、リクエストが存在することをInvokeがどのように知っているかについて混乱していました。実際、上記のtrueおよびclass $ proxy0は多くの質問を解決し、以下に言及する$ proxy0のソースコードと相まって、動的プロキシの疑問を完全に解決できます。
上記のコードと結果から、図のようにinvoke()メソッドを呼び出さなかったことがわかりますが、この方法は実行されました。以下のプロセス全体を分析しましょう。
クライアントのコードから判断すると、新しいProxyInstanceメソッドをブレークスルーとして使用できます。まず、プロキシクラスのNewProxyInstanceメソッドのソースコードを見てみましょう。
public staticオブジェクトNewProxyInstance(classloaderローダー、クラス<? } / * *設計されたプロキシクラスを検索または生成します。 */ class cl = getProxyclass(ローダー、インターフェイス); / * *設計された呼び出しハンドラーを使用してコンストラクターを呼び出します。 * / try { / * *プロキシソースコードには次の定義があります。 * Consは、InvocationHandler Typeの正式なパラメーターを備えたコンストラクターメソッド*/ Constructor Cons = Cl.GetConstructor(ConstructOrParams); return(object)cons.newinstance(new object [] {h}); } catch(nosuchmethodexception e){新しいinternalerror(e.tostring()); } catch(Illegalaccessexception e){新しいinternalerror(e.tostring()); } catch(instantiationexception e){new internalerror(e.tostring()); } catch(InvocationTargetException e){Throw New InternerError(e.toString()); }} proxy.newproxyinstance(classloader loader、class <?
(1)パラメーターローダーとインターフェイスに基づいてメソッドgetProxyclass(ローダー、インターフェイス)を呼び出し、プロキシクラス$ proxy0。$ proxy0クラスを作成し、インターフェイスインターフェイスを実装し、プロキシクラスを継承します。
(2)コンストラクターに$ proxy0をインスタンス化し、Dynamicsubjectをパスすると、$ proxy0は親クラスプロキシのコンストラクターを呼び出し、次のようにhに値を割り当てます。
class proxy {InvocationHandler H = null;保護されたプロキシ(InvocationHandler H){this.h = h; } ...}プロキシの$ proxy0を継承するソースコードを見てみましょう。
パブリックファイナルクラス$ proxy0拡張プロキシ実装件名{private static method m1;プライベート静的メソッドM0;プライベート静的メソッドM3;プライベート静的メソッドM2; static {try {m1 = class.forname( "java.lang.object")。getMethod( "equals"、new class [] {class.forname( "java.lang.object")}); m0 = class.forname( "java.lang.object")。getMethod( "hashcode"、new class [0]); m3 = class.forname( "***。realsubject")。getMethod( "request"、new class [0]); m2 = class.forname( "java.lang.object")。getMethod( "toString"、new class [0]); } catch(nosuchmethodexception nosuchmethodexception){新しいnosuchmethoderror(nosuchmethodexception.getmessage()); } catch(classNotFoundException classNotFoundException){新しいnoclassdeffounderror(classNotFoundException.getMessage()); }} // static public $ proxy0(InvocationHandler InvocationHandler){super(invocationHandler); } @Override public final boolean equals(object obj){try {return((boolean)super.h.invoke(this、m1、new object [] {obj})).booleanvalue(); } catch(スロー可能なスロー可能){新しいundeClaredThrowableException(スロー可能)を投げる; }} @Override public final int hashcode(){try {return((integer)super.h.invoke(this、m0、null))。intvalue(); } catch(スロー可能なスロー可能){新しいundeClaredThrowableException(スロー可能)を投げる; }} public final void request(){try {super.h.invoke(this、m3、null);戻る; } catch(error e){} catch(throwable throwable){new new undeclaredthrowableException(投げ可能); }} @Override public final String toString(){try {return(string)super.h.invoke(this、m2、null); } catch(スロー可能なスロー可能){新しいundeClaredThrowableException(スロー可能)を投げる; }}}次に、結果の$ proxy0インスタンスを被験者にキャストし、被験者への参照を割り当てます。 subject.request()メソッドが実行されると、$ proxy0クラスのリクエスト()メソッドが呼び出され、親クラスプロキシのhのinvoke()メソッドが呼び出されます。つまり、InvocationHandler.Invoke()です。
PS:1。注意すべきことの1つは、プロキシクラスのGetProxyclassメソッドがプロキシのクラスクラスを返すことです。その理由は、最初に私が低レベルの間違いを犯したため、返品は「プロキシクラスのクラスクラス」であると考えています。 GetProxyclassのソースコードを見ることをお勧めします。これは非常に長い=です。 =
2。$ proxy0のソースコードから、ダイナミックプロキシクラスは、ディスプレイが定義されたインターフェイスのメソッドをプロキシするだけでなく、Javaのルートクラスオブジェクトの継承()、HashCode()、およびtoString()の3つのメソッドをプロキシし、これら3つの方法のみをプロキシします。
Q:これまでのところ、まだ質問があります。 Invokeメソッドの最初のパラメーターはプロキシのインスタンスです(正確には、$ proxy0のインスタンスが最終的に使用されます)が、使用は何ですか?または、プログラムに機能がどのように表示されますか?
A:現在のレベルから、このプロキシパラメーターは効果がありません。動的プロキシメカニズム全体では、InvocetyHandlerのInvokeメソッドのプロキシパラメーターは使用されません。渡されたパラメーターは、実際にはプロキシクラスのインスタンスです。プログラマーにInvokeメソッドの反射を使用して、プロキシクラスに関する情報を取得できるようにすることができると思います。
要約します
上記は、InvocationHandlerのInvoke()メソッドに関するこの記事の内容全体です。私はそれが誰にでも役立つことを願っています。興味のある友達は引き続きこのサイトを参照できます:
Spring Static Proxyおよび動的プロキシコードの詳細な説明
スプリングフレームワーク依存関係インジェクションメソッドの例
springMVCシンプルログイン例のJavaプログラミングの実装
欠点がある場合は、それを指摘するためにメッセージを残してください。