プロキシモデルは、一般的に使用されるJava設計モデルです。その特徴は、プロキシクラスとデリゲートクラスが同じインターフェイスを持っていることです。プロキシクラスは、主にメッセージの前処理、メッセージのフィルタリング、デリゲートクラスへのメッセージの転送、およびイベント後のメッセージの処理を担当します。通常、プロキシクラスとデリゲートクラスの間には関連性があります。プロキシクラスのオブジェクトは、デリゲートクラスのオブジェクトに関連付けられています。プロキシクラス自体のオブジェクトは、本当にサービスを実装するのではなく、代表クラスオブジェクトの関連する方法を呼び出すことで特定のサービスを提供します。
Javaのさまざまな動的プロキシ実装の比較
インタフェース
インターフェイスaddInterface {int add(int a、int b);} interface subinterface {int sub(int a、int b);}実装クラス
クラスの算術実装addinterface、subinterface {@override public int sub(int a、int b){return ab; } @override public int add(int a、int b){return a+b; }}方法1: JDKに組み込まれた動的プロキシ
1。実装方法
JAVAがJDK1.3後に導入した動的プロキシメカニズムにより、ランタイム中にプロキシクラスを動的に作成できます。動的プロキシを使用してAOPを実装するには、プロキシクラス、プロキシクラスインターフェイス、織りデバイス、および呼び出しハンドラーの4つの役割が必要です。織りデバイスは、インターフェイス反射メカニズムを使用してプロキシクラスを生成し、このプロキシクラスにコードを織ります。プロキシクラスは、AOPで言及されているターゲットです。 InvocationHandlerは、アドバイスとポイントカットを含むセクションです。
2。VinvocationHandlerインターフェイスの実装
クラスJDKDPQueryHandlerは、InvocationHandlerを実装しています{private Arithmetic Real; public jdkdpqueryhandler(arithmetic real){this.real = real; } @Override public object invoke(object proxy、method method、object args)throws throwable {string methodname = method.getname(); system.out.println(method); system.out.println( "メソッド:" + methodname + "start、parameters:" + arrays.aslist(args));オブジェクトresult = method.invoke(real、args); system.out.println( "メソッド:"+methodname+"ends、result:"+result);返品結果; }}3.プロキシクラスを作成し、プロキシクラスを呼び出します
public class main {private static int a = 4、b = 2; public staticオブジェクトCreateJDKProxy(arithmetic Real){object proxyarithmetic = proxy.newproxyinstance(real.getClass()。getClassLoader()、real.getClass()。 proxyarithmeticを返します。 } public static void main(string [] args){arithmetic real = new arithmetic();オブジェクトproxyarithmetic = createjdkproxy(real); ((addinterface)proxyarithmetic).add(a、b); ((subinterface)proxyarithmetic).sub(a、b); }}方法2:動的バイトコード生成(CGLIB)
1。実装方法
エンハンサーとMethodEnterceptor。エンハンサーを使用して、指定されたクラスを継承し、指定されたインターフェイスを実装できるクラスを動的に生成できます。同時に、エンハンサーはクラスを生成する前にコールバックを指定する必要があります。クラスメソッドが呼び出されると、メソッドの実行がこのコールバックに割り当てられます。 MethodEnterceptorは、コールバックから継承されたより広く使用されているインターフェイスであり、メソッド宣言は1つしかありません。
2。インターフェイスInvocationHandler(JDK)とInterface MethodEnterceptor(CGLIB)の比較
public interface methodEnterceptor callback {public object intercept(object obj、java.lang.reft.methodメソッド、オブジェクト[] args、methodproxyプロキシ)スロー可能。 } public interface rivocationhandler {public object invoke(object proxy、method method、object [] args)スロー可能; }パラメーター構成に関しては、MethodEnterceptorの入力パラメーターは、InvocationHandlerよりも1多いです。実際、最初の3つのパラメーターオブジェクトの意味は、InvocationHandlerの意味と同じです。
最初のパラメーターは、コールメソッドがどのオブジェクトから来るかを示します。
2番目のパラメーターは、メソッドを呼び出すメソッドオブジェクトを表します。
3番目のパラメーターは、この呼び出しの入力パラメーターリストを表します。
追加のパラメーターはMethodProxyタイプであり、メソッドオブジェクトを置き換えるためにCGLIBによって生成されるオブジェクトである必要があります。 MethodProxyを使用すると、JDK独自の方法を直接呼び出すよりも効率が向上します。
3.実現1
MethodInterceptorインターフェイスの実装
クラスCGLIBDPQUERYINTERCEPTORを実装MethodEnterceptor {private Arithmetic Real; public cglibdpqueryInterceptor(arithmetic real){this.real = real; } @Override public Object intercept(Object Target、Method Method、Object args、MethodProxy Proxy)Throws {string methodname = method.getName(); system.out.println(method); system.out.println( "メソッド:" + methodname + "start、parameters:" + arrays.aslist(args)); //オブジェクトresult = method.invoke(real、args); //両方のメソッドがオブジェクトresult = proxy.invoke(real、args)を取得できます。 system.out.println( "メソッド:"+ methodname+ "ends、result:"+ result);返品結果; }}プロキシクラスを作成し、プロキシクラスを呼び出します
public class main {private static int a = 4、b = 2; public staticオブジェクトCreatecglibproxy(arithmetic real){enchancer enthancer = new Enhancer(); Enhancer.setCallback(新しいcglibdpqueryInterceptor(REAL)); Enhancer.setInterfaces(real.getClass()。getInterfaces()); return enthancer.create(); } public static void main(string [] args){arithmetic real = new arithmetic();オブジェクトproxyarithmetic = createcglibproxy(real); ((addinterface)proxyarithmetic).add(a、b); ((subinterface)proxyarithmetic).sub(a、b); }}MethodProxyは、機能を実行するときに2つの方法を提供することに注意してください。
パブリックオブジェクトの呼び出し(オブジェクトOBJ、オブジェクト[] args)スローが投げられるパブリックオブジェクトInvokeSuper(Object obj、object [] args)スロー可能
その中で、Javadocは、このInvoke()メソッドは同じクラスの他のオブジェクトの実行に使用できると述べています。つまり、この方法のOBJは同じクラスの別のオブジェクトに渡す必要があります(上記の方法は、算術クラスの異なるオブジェクトに渡すことです)。慎重に考えると、パブリックオブジェクトインターセプト(オブジェクトターゲット、メソッドメソッド、オブジェクト[] arg、methodproxyプロキシ)のターゲットが実装されたプロキシオブジェクトであることがわかります。 add()メソッドがターゲットを介して呼び出されると、intercept()メソッドが呼び出されるようにトリガーされます。 Method.Invoke(ターゲット、ARGS)がIntercept()メソッドで呼び出された場合、再びADD()メソッドを呼び出して、無限の再帰ループをもたらします。ただし、method.invoke(real、args)を実行すると、実際のターゲットとターゲットは同じクラスの異なるオブジェクトであるため、リアルは実際の論理的トピックであり、ターゲットは実際のトピックのプロキシです。
シミュレートする例は次のとおりです。
Interface solveinterface {void solve();} class real implements solveinterface {public void solve(){system.out.println( "real solve!"); }} classターゲットはreal {private object obj; public void setObject(object obj){this.obj = obj; } private void invoke(){try {method method = solveinterface.class.getMethod( "solve"、new class [] {}); method.invoke(obj、new class [] {}); } catch(Exception e){e.printstacktrace(); }} public void solve(){system.out.println( "ターゲットソルブ!"); invoke(); }} public class main {public static void main(string [] args)スロー例外{ターゲットターゲット= newターゲット();ターゲット.setObject(new real()); // recort // target.setObject(ターゲット); //環状呼び出しはターゲット.solve(); }}実際、メソッドのInvoke()メソッドは、OBJタイプ、つまり多型に応じて対応するsolve()メソッドを呼び出します。
4。実現2
MethodInterceptorインターフェイスの実装
クラスcglibdpqueryInterceptor MethodEnterceptor {@Override public object intercept(Objectターゲット、メソッドメソッド、オブジェクト、args、methodproxyプロキシ)スロースロー可能{string methodname = method.getname(); system.out.println(method); system.out.println(method); system.out.println( "メソッド:" + methodname + "start、parameters:" + arrays.aslist(args)); //クラス情報を印刷:ターゲット.getClass(); Object result = proxy.invokesuper(ターゲット、args); system.out.println( "メソッド:"+methodname+"ends、result:"+result);返品結果; }}プロキシクラスを作成し、プロキシクラスを呼び出します
public class main {private static int a = 4、b = 2; public static object createcglibproxy(){enthancer enthancer = new enthancer(); Enhancer.setCallback(new cglibdpqueryInterceptor()); Enhancer.setsuperclass(arithmetic.class); return enthancer.create(); } public static void main(string [] args){arithmetic real = new arithmetic();オブジェクトproxyarithmetic = createcglibproxy(); ((addinterface)proxyarithmetic).add(a、b); ((subinterface)proxyarithmetic).sub(a、b); }}スーパークラスが設定されているため(つまり、プロキシクラスの親クラスは算術)、プロキシクラスが継承され、算術がインターフェイスを実装しているため、エンハンサーは実装2にインターフェイスを設定しないことに注意してください。これを証明するために、methodEnterceptorのインターセプト方法でターゲットのクラス情報を印刷できます。 CGLIBプロキシクラスの親クラスが異なることがわかります。次のように:
実装1:
パブリッククラスcom.test.arithmetic $$ enthancerbycglib $ 4fa786eb拡張java.lang.object
実装2:
パブリッククラスcom.test.arithmetic $$ enthancerbycglib $ 4fa786eb extends com.test.arithmetic
方法3: Javassistは動的プロキシを生成します(エージェント工場の作成または動的コード作成)
Javassistは、Bytecodeを編集するためのフレームワークであり、Bytecodeを非常に簡単に動作させることができます。ランタイム中にクラスを定義または変更できます。 Javassistを使用してAOPを実装する原則は、ByteCodeがロードされる前にカットする必要がある方法を直接変更することです。これは、CGLIBを使用してAOPを実装するよりも効率的であり、多くの制限はありません。実装の原則は次のとおりです。
実装1:
インターフェイスの実装
クラスjavassistdpqueryhandlerを実装Methodhandler {@override public object invoke(objectターゲット、メソッドメソッド、メソッドプロキシ、オブジェクト[] arg)スロー{string methodname = method.getname(); system.out.println(method); system.out.println(method); system.out.println( "メソッド:" + methodname + "start、parameters:" + arrays.aslist(args));オブジェクトresult = proxy.invoke(ターゲット、args); system.out.println( "メソッド:"+methodname+"ends、result:"+result);返品結果; }}プロキシクラスを作成し、プロキシクラスを呼び出します
public class main {private static int a = 4、b = 2; public static object createjavassistproxy()throws Exception {proxyFactory Factory = new ProxyFactory(); Factory.setsuperclass(arithmetic.class); Factory.sethandler(new JavassistDpqueryHandler()); return factory.createclass()。newInstance(); } public static void main(string [] args)throws exception {arithmetic real = new arithmetic();オブジェクトproxyarithmetic = createjavassistproxy(); ((addinterface)proxyarithmetic).add(a、b); ((サブインターフェイス)proxyarithmetic).sub(a、b); }}注:MethodHandlerインターフェイスのInvokeメソッドの定義は次のとおりです。
パブリックオブジェクトの呼び出し(オブジェクトターゲット、メソッドメソッド、メソッドプロキシ、オブジェクト[] args)
メソッドは、メソッドを呼び出すメソッドオブジェクトを表します。プロキシは、メソッドをプロキシクラスで生成および置き換えるオブジェクトです。それ以外の場合、method.invoke(ターゲット、args)を使用すると、無限のループ呼び出しが生成されます。
実装2:
動的なJavassistを使用する一般的なプロキシプロセスは、以前の方法とわずかに異なります。 Javassistの内部では、動的Javaコードを生成してBytecodeを生成できます。この方法で作成された動的プロキシは非常に柔軟であり、実行時にビジネスロジックを生成することさえできます。
//カスタムインターセプターインターフェイスInterceptorHandler { /***動的プロキシオブジェクトの方法を呼び出すと、この方法が反映されます。この方法の実装には、AOPのような操作と操作後の操作を追加できます。次のコードがこのメソッドに追加された場合のみ*プロキシメソッドが実行され、返品値がプロキシに返され、最終的にプログラムに返されます* @param objオブジェクトプロキシオブジェクト*プロキシオブジェクト* @param argsオブジェクト[]プロキシオブジェクトのパラメーター* @return showable*/ publun objectのreturn objects( obj、メソッドメソッド、オブジェクト[] args)スロー可能。 } //インターセプタークラスの実装InterceptorHandlerimpl InterceptorHandler {@Override public Object invoke(object obj、method method、object [] args)throws {string methodname = method.getname(); system.out.println(method); system.out.println(method); system.out.println( "メソッド:" + methodname + "start、parameters:" + arrays.aslist(args));オブジェクトresult = method.invoke(obj、args); system.out.println( "メソッド:"+methodname+"ends、result:"+result);返品結果; }} class myproxyimpl { / **ダイナミックプロキシクラスのクラス名サフィックス* / private final static string proxy_class_name_suffix = "$ myproxy_"; / **インターセプターインターフェイス*/プライベート最終的な静的文字列interceptor_handler_interface = "com.test.interceptorhandler"; / **クラス名の重複を防ぐための動的プロキシクラスのクラス名インデックス*/ private static int proxyclassindex = 1; /***ユーザーに公開された動的プロキシインターフェイスは、特定のインターフェイスの動的プロキシオブジェクトを返します。 Note that the implementation of this proxy must be used with the com.cuishen.myAop.InterceptorHandler interceptor*, that is, if the user wants to use this dynamic proxy, he must first implement the com.cuishen.myAop.InterceptorHandler interceptor interface* @param interfaceClassName String The interface class name to be dynamic proxy, eg test.StudentInfoService * @param Classtoproxy stringインターフェイスの実装クラスのクラス名は動的にプロキシ化されます。 @throws notfoundexception * @throws can cancompileexception * @throws classnotfoundexception * @seee com.cuishen.myaop.interceptorhandler */ public staticオブジェクトNewProxyInstance(String InterfaceClassName、String classtoproxy、String Interceptorhandlerimpllasscectioncections notfoundException、can councompileexception、classNotFoundException {class interfaceclass = class.forname(interfaceClassName); class interceptorhandlerimplclass = class.forname(interceptorhandlerimplclassname); DynamicImplementsInterface(Classtoproxy、InterfaceClass、InterceptorHandlerimplclass)を返します。 } /** *プロキシ化されるインターフェイスの動的実装 * @param classtoproxy stringインターフェイスの実装クラスのクラス名は動的にプロキシ化されます。インターセプターインターフェイスのユーザーが提供する実装クラス * @returnオブジェクト特定のインターフェイスの動的プロキシオブジェクト * @throws notfoundexception * @throws can counccompileexception * @throws Illegalaccessexception */ private static object dynamicimplementsinterface(classtoproxy、class dynamplements interpcretrers classtroxy、class dynamicimplements interface) notFoundException、Can combinexception、instantiationexception、legalaccessexception {classpool cp = classpool.getDefault();文字列interfaceName = interfaceClass.getName(); //プロキシクラスのクラス名を動的に指定しますproxyclassName = interfaceName + proxy_class_name_suffix + proxyclassindex ++; //実装されるインターフェイスのパッケージ名 +インターフェイス名String interfacenamepath = interfaceName; ctclass ctinterface = cp.getctclass(interfacenamepath); ctclass cc = cp.makeclass(proxyclassname); cc.addinterface(ctinterface);方法[] methods = interfaceclass.getMethods(); for(int i = 0; i <methods.length; i ++){method method = methods [i]; dynamicimplementsmethodsfrominterface(Classtoproxy、CC、Method、Interceptorhandlerimplclass、i); } return(object)cc.toclass()。newInstance(); } /***メソッドメソッドの動的実装* @param classtoproxy文字列インターフェイスの実装クラスのクラス名は動的にプロキシ化されます。 InterceptorClassクラスユーザー* @Param MethodIndex Inted Indexが提供するインターセプター実装クラス実装するメソッドのインデックス* @throws CancompileeException*/ private void dynamicimplementsmethodsfrominterface(String classtoproxy、ctclass実装者、MethodtoImpl、クラスinterceptorclass、interptorclass、interctorclass interctodows interce generatemethodcode(classtoproxy、methodtoimpl、interceptorclass、methodindex); ctmethod cm = ctnewmethod.make(methodCode、実装者); emfincyer.addmethod(cm); } /***メソッド本体を動的に組み立てます。もちろん、プロキシのメソッド実装は単純なメソッドコピーではありませんが、インターセプターの呼び出しメソッドを反映し、受信したパラメーター* @param classtoproxy stringインターフェイスの実装クラスのクラス名を動的にプロキシ化するように渡します。 @Param InterceptorClassクラスユーザーが提供するインターセプター実装クラス* @Param MethodIndex Intインデックス実装するメソッド* @return string Dynamic Assembly Methodの文字列*/ private static string generatemethodcode(string classtoproxy、methodtoimpl、class interceptorclass、intname = methodname = methodname impl.getname( string methodreturntype = methodtoimpl.getReturnType()。getName(); class [] parameters = methodtoimpl.getParametertypes(); class [] exceptionTypes = methodtoImpl.getExceptionTypes(); StringBuffer ExceptionBuffer = new StringBuffer(); //アセンブリメソッドの例外宣言if(exceptionTypes.length> 0)ExceptionBuffer.Append( "Throws"); for(int i = 0; i <exceptiontypes.length; i ++){if(i!= exceputtytepes.length -1)exceptionbuffer.append(exceptionTypes [i] .getName())。append( "、"); else exceptionbuffer.append(exceptionTypes [i] .getName()); } stringbuffer parameterbuffer = new StringBuffer(); //アセンブリメソッドのパラメーターリスト(int i = 0; i <parameters.length; i ++){class parameter = parameters [i]; string parametertype = parameter.getName(); //動的指定メソッドの変数名パラメーター文字列refname = "a" + i; if(i!= parameters.length -1)parameterbuffer.append(parametertype).append( " + refname).append("、 "); else parameterbuffer.append(parametertype).append(" + refname); } stringbuffer methoddeclare = new StringBuffer(); //メソッド宣言は、インターフェイスを実装するメソッドであるため、public methodedeclare.append( "public").append(methodreturntype).append( "").append( "(")。 string interceptorimplname = interceptorclass.getName(); // methodbody methoddeclare.append(interceptor_handler_interface).append( "interceptor = new").append(interceptorimplname).append( "();/n"); //ユーザーのインターセプターインターフェイスMethodDeclare.Append( "object Recroneobj = interceptor.invoke(class.forname(/" " + classtoproxy +"/")。newinstance()、class.forname(/" " + classtoproxy +"/")。 if(parameters.length> 0)methoddeclare.append( "new object {"); MethodDeclare.Append( "($ w)a" + i + "); else methoddeclare.append( "null);/n"); //コールインターセプターのリターン値をラップif(methodtoimpl.getReturnType()。isprimitive()){if(methodtoimpl.getReturnType()。equals(boolean.type))methoddeclare.append( "return((boolean)returnobj).booleanvalue();/n";/n ";/n"; else if(methodtoimpl.getReturnType()。equals(integer.type))methoddeclare.append( "return((integer)returnobj).intvalue();/n"); else if(methodtoimpl.getReturnType()。equals(long.type))methoddeclare.append( "return((long)returnobj).longvalue();/n"); else if(methodtoimpl.getReturnType()。equals(float.type))methoddeclare.append( "return((float)returnobj).floatvalue();/n"); else if(methodtoimpl.getReturnType()。equals(double.type))methoddeclare.append( "return(" return( "return(" return();/n "); else if(methodtoimpl.getreturntype()。equals(double.type)) methodDeclare.append("return("return("return("return("return("return();/n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("return("return("methodToImpl.getReturnType().equals(Double.TYPE)) methoddeclare.append( "return(" return( "return(" return( "return)(" methodtoimpl.getReturnType()。equals(double.type))methoddeclare.append( "return(" return( "return(" return( "methodtoimpl.getreturntype)。 MethodDeclare.Append( "return(" return( "return(" return( "methodtoimpl.getReturnType)。equal((double)returnobj).doublevalue();/n"); else if(methodtoimpl.getReturnType() if(methodtoimpl.getReturnType()。equals(byte.type))methoddeclare.append( "return((byte)returnobj).bytevalue();/n") ((byte)returnobj).bytevalue();/n "); else if(methodtoimpl.getReturnType()。equals(short.type))methoddeclare.append(" return((short)returnobj).shortvalue(();/n ");} reth {methoddeclare.append ")returnobj;/n")MethodDeclare.out.println();プロキシクラスが必要です、ユーザー定義インターセプターの実装クラス名オブジェクトProxyArithmetic = myProxyimpl.newproxyInstance( "com.test.arithmetict.arithmetic"、 "com.test.interceportorhandlerimpl"); ((arithmeticterface)proxyarithmetic).sub(a、b)}}次のようにインターフェイスを動的に実装するためのコードを印刷します。
public int add(int a0、int a1){com.test.interceptorhandler interceptor = new com.test.interceptorhandlerimpl(); object returnobj = interceptor.invoke(com.test.arithmetic ")。newinstance()、class.forname(" com.test.arithmetic( "com.test.arithmethet(" "")、com.test.arithmethet( ")。 object [] {($ w)a0、($ w)a1}); return((integer)returnobj).intvalue();} public int sub(int a0、int a1){com.test.interceptorhandler intercector = new com.test.InterceptorHandlerimpl(); Object Returnobj =; Interceptor.Invoke(class.forname( "com.test.arithmetic")。newInstance()、class.forname( "com.test.arithmetic")。getMethods()[1]、new object {($ w)a0、($ w)a1});上記は、Javaの動的プロキシ実装メカニズムの詳細な紹介であり、すべての人の学習に役立つことを願っています。