静的プロキシ、プロキシ クラスとプロキシされるクラスは同じインターフェイスを実装し、プロキシ クラスはプロキシされるクラスへの参照も保持します。 . このように、プロキシクラスのメソッドを呼び出す必要がある場合は、プロキシクラスのメソッドを呼び出すことで実行できます。
例: リーダーの仕事は、会議を開催し、従業員を評価することだとします。
最初にインターフェイスを定義します:
package com.sharpcj; public interface IWork { void meeting(); int evaluate(String name); }
次に、リーダーシップ クラスを定義します:
package com.sharpcj; import java.util.Random; public class Leader implements IWork { @Override public void meeting() { System.out.println("领导早上要组织会议"); } @Override public int evaluate(String name) { int score = new Random(System.currentTimeMillis()).nextInt(20) + 80; System.out.println(String.format("领导给%s的考评为%s分", name, score)); return score; } }
秘書クラス:
package com.sharpcj; public class Secretary implements IWork { private Leader mLeader; public Secretary(Leader mLeader) { this.mLeader = mLeader; } @Override public void meeting() { System.out.println("秘书先给老板准备材料"); mLeader.metting(); } @Override public int evaluate(String name) { return mLeader.evaluate(name); } }
テストクラス:
package com.sharpcj; public class TestApp { public static void main(String[] args) { Leader leader = new Leader(); Secretary secretary = new Secretary(leader); secretary.meeting(); secretary.evaluate("Joy"); } }
実行結果:
これコード 非常に簡単です。Secretary
クラスの会議メソッドを呼び出すときに、Leader
クラスの会議メソッドを呼び出したことに注意してください。その前に、このメソッドも拡張しました。この時点で混乱する人もいるかもしれませんが、これは少しデコレータ パターンのようです。どうしたの?
実際、デコレータ パターンとプロキシ パターンの間にはまだ多くの違いがあります。 Decorator パターンはオブジェクトにメソッドを動的に追加することに重点を置いているのに対し、Proxy パターンはオブジェクトへのアクセスを制御することに重点を置いています。つまり、プロキシ パターンを使用すると、プロキシ クラスはオブジェクトの特定の情報をクライアントから隠すことができます。したがって、プロキシ パターンを使用する場合は、プロキシ クラスでオブジェクトのインスタンスを作成することがよくあります。そして、デコレータ パターンを使用するときの通常のアプローチは、元のオブジェクトをパラメータとしてデコレータのコンストラクタに渡すことです。
これらの違いは別の文で要約できます: プロキシ パターンを使用すると、プロキシと実際のオブジェクトの関係は通常コンパイル時に決定されますが、デコレータは実行時に再帰的に実行できます。構造。
# まず、2 つの UML クラス図の違いを見てみましょう:
エージェント モード:
デコレータ モード:
両方の疑似コード:エージェント モード:
Interface Subject { void doAction() } public class RealSubject implements Subject{ @Override public void doAction() {}; } public class Proxy implements Subject{ private RealSubject realSubject; public Proxy(RealSubject realSubject) { //关系在编译时确定 this.realSubject = realSubject; } @Override public void doAction() { …. realSubject.doAction(); …. } }
デコレータ パターン;
Interface Component { void doAction() } public class ConcreteComponent implement Component { @Override public void doAction() {}; } public class Decorator implements Component { private Component component; public Decorator(Component component) { //关系在编译时确定 this.component = new component; } public void doAction() { …. component.doAction(); …. } }
JDK 動的プロキシ: リフレクション メカニズムを使用してプロキシ インターフェイスを実装するクラスを生成し、特定のメソッドを呼び出す前に InvokeHandler を呼び出してそれを処理します。
CGlib ダイナミック プロキシ: ASM (オープン ソース Java バイトコード編集ライブラリ、バイトコードを操作する) オープン ソース パッケージを使用して、プロキシ オブジェクト クラスのクラス ファイルをロードし、処理するバイトコード サブクラスを変更することでプロキシ オブジェクト クラスを生成します。
違い: JDK エージェントはインターフェイスを実装するクラスのエージェントのみを生成できますが、CGlib はクラスのエージェントを実装し、指定されたクラスのサブクラスを生成し、その中のメソッドをオーバーライドします。これは、 class 実装メソッドは、最終的に変更されたクラスをプロキシすることはできません。
上記の例を例として取り上げます。 まず、
InvocationHandler インターフェイスを実装するクラスを定義し、 invoke メソッドを実装します:
package com.sharpcj; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("object: " + object.getClass().getSimpleName()); System.out.println("proxy: " + proxy.getClass().getSimpleName()); if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return method.invoke(object, args); } else if ("evaluate".equals(method.getName())) { if(args[0] instanceof String) { if ("James".equals(args[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return method.invoke(object, args); } return null; } }
Proxy.newProxyInstance() メソッドを通じてプロキシ オブジェクトを作成します:
package com.sharpcj; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { /*Leader leader = new Leader(); Secretary secretary = new Secretary(leader); secretary.meeting(); secretary.evaluate("Joy");*/ Leader leader = new Leader(); IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(leader)); proxy.meeting(); proxy.evaluate("Joy"); proxy.evaluate("James"); } }
出力結果:
WorkInvocationHandler クラスを通じて、Leader クラスのメソッドの実装をプロキシすることもできることがわかります。実際、実装するのは任意のメソッドの実装ですが、単にプロキシ オブジェクトを作成するときに渡します。これは、Iwork インターフェイスおよび Leader クラス オブジェクトです。次の例を見てください:ここで注意する必要があるのは: InvocationHandler インターフェイスの呼び出しメソッドの最初のパラメーター プロキシは、呼び出しメソッドのオブジェクトではありません。では、このパラメーターは何でしょうか。コードでは、プロキシのクラス名を出力するために、対応する出力を特別に追加しました。実際、プロキシはプロキシ オブジェクトそのものであり、その意味は、invoke メソッドでプロキシ オブジェクトを返し、その後連続呼び出しを行うことができるということです。
package com.sharpcj.proxytest; public interface IWork { IWork work(String subject); }
package com.sharpcj.proxytest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("work".equals(method.getName())){ System.out.println("--- work: " + args[0]); return proxy; } return null; } }
package com.sharpcj.proxytest; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { IWork worker = (IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(new IWork() { @Override public IWork work(String subject) { return null; } })); worker.work("AAA").work("BBB").work("CCC"); } }
結果は次のとおりです:
首先添加 cglib 依赖
build.gradle 文件:
... dependencies { // 引入 cglib 库 compile 'cglib:cglib:3.1' testCompile group: 'junit', name: 'junit', version: '4.12' } ...
前面说了,cglib 针对类进行代理,我们以上面的 Leader 类为例,先创建一个类实现 MethodInterceptor
接口:
package com.sharpcj; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LeaderMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return methodProxy.invokeSuper(o, objects); } else if ("evaluate".equals(method.getName())) { if(objects[0] instanceof String) { if ("James".equals(objects[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return methodProxy.invokeSuper(o, objects); } return null; } }
测试代码:
package com.sharpcj; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\temp\code"); //保存生成的 class 文件 Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程 enhancer.setSuperclass(Leader.class); // 设置enhancer对象的父类 enhancer.setCallback(new LeaderMethodInterceptor()); // 设置enhancer的回调对象 Leader proxy= (Leader)enhancer.create(); // 创建代理对象 // 通过代理对象调用目标方法 proxy.meeting(); proxy.evaluate("Joy"); proxy.evaluate("James"); } }
结果如下:
MethodInterceptor
接口只有一个 intercept
方法,这个方法有4个参数:
1)obj表示增强的对象,即实现这个接口类的一个对象;
2)method表示要被拦截的方法;
3)args表示要被拦截方法的参数;
4)proxy表示要触发父类的方法对象;
需要注意的是:实际调用是 methodProxy.invokeSuper()
, 如果使用 invoke()
方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。
以上がJava動的プロキシと静的プロキシの分析例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。