Java動的プロキシのサンプルコードを深く理解する
この記事では、Java ダイナミック プロキシを深く理解するための関連情報を主に紹介します。必要な方は参考にしてください。
Java ダイナミック プロキシを理解したい場合は、まずプロキシについて理解する必要があります。デザインパターンはGofにまとめられているということを知っておく必要があります。 23のデザインパターンの中には、プロキシと呼ばれるオブジェクト構造パターンがあります。 ダイナミックプロキシにおけるプロキシは、このデザインパターンを指します。 23種類のデザインパターンのうち、いわゆる
エージェントモードと「デコレーションモード」は同じものだと私は考えています。 23 のデザインパターンのうち、これら 2 つのパターンの類似点と相違点については、インターネット上にもいくつかの記事がありますが、抽象化すると、確かにこの 2 つのパターンを区別することができます。あるレベルでは、これら 2 つのパターンはまったく同じではないと思います。したがって、プロキシモードを学べば、デコレーションモードもマスターできることになります。
プロキシモード簡単に言えば、プロキシモードはオブジェクトをラップすることです。パッケージ化後に生成されたオブジェクトは、元のオブジェクトと同じメソッドリストを持ちますが、各メソッドをラップすることができます。
package common; public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class SubjectImplProxy implements Subject{ private Subject target; public SubjectImplProxy(Subject target) { this.target=target; } @Override public void sayHi() { System.out.print("say:"); target.sayHi(); } @Override public void sayHello() { System.out.print("say:"); target.sayHello(); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=new SubjectImplProxy(subject); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
このコードはまず、2 つのメソッドを持つ Subjectinterface
を定義します。 次に、Subject インターフェイスを実装するために SubjectImpl クラスが定義され、その 2 つのメソッドが実装されます。ここにはまったく問題ありません。
次に、別の SubjuectImplProxy クラスを定義し、Subject インターフェイスも実装します。この SubjectImplProxy クラスの目的は、SubjectImpl クラスのインスタンスをラップすることです。これは、SubjectImpl のインスタンスを保存するために、
変数target を内部的に定義します。 SubjectImplProxy は、インターフェイスによって指定された 2 つのメソッドも実装します。その実装バージョンでは、SubjectImpl の実装を呼び出しますが、独自の処理ロジックを追加します。 このコードは理解するのが難しくないと思います。SubjectImpl をラップして、出力コンテンツにプレフィックスを追加する機能を実現します。このプロキシ方式は静的プロキシと呼ばれます。
動的プロキシ上記のデモから、静的プロキシの欠点を理解するのは難しくありません。SubjectImpl の 2 つのメソッドに同じパッケージ化を使用していますが、SubjectImplProxy で同じパッケージ化ロジックを 2 回記述する必要があります。また、将来新しいメソッドが Subject インターフェイスに追加された場合、SubjectImplProxy はすべてのメソッドを同じようにラップする可能性がありますが、SubjectImplProxy も新しい実装を追加する必要があります。
ここで、上の例の静的プロキシを動的プロキシに変更します。違いを見てみましょう:
package common; import java.lang.invoke.MethodHandle; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class ProxyInvocationHandler implements InvocationHandler{ private Subject target; public ProxyInvocationHandler(Subject target) { this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print("say:"); return method.invoke(target, args); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
main メソッドだけを見ると、2 行目だけが前の静的プロキシと異なります。また、subjectProxy プロキシ オブジェクトも生成されますが、生成されるコードのみが異なります。静的プロキシは SubjectImplProxy のインスタンスを直接作成しますが、動的プロキシは java.lang.reflect.Proxy.newProxyInstance() メソッドを呼び出します。このメソッドのソース コードを見てみましょう:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass(loader, interfaces); //获取代理类的Class /* * Invoke its constructor with the designated invocation handler. */ try { Constructor cons = cl.getConstructor(constructorParams); //constructorParams是写死的:{ InvocationHandler.class },上边返回的代理类Class一定是extends Proxy的,而Proxy有一个参数为InvocationHandler的构造函数 return cons.newInstance(new Object[] { h }); //这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里,当我们调用代理类的任何方法时, 实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数 } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
Class< ; 上記 ?> cl = getProxyClass(loader, Interfaces); getProxyClass メソッドが呼び出されます:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { if (interfaces.length > 65535) { //因为在class文件中,一个类保存的接口数量是用2个字节来表示的,因此java中一个类最多可以实现65535个接口 throw new IllegalArgumentException("interface limit exceeded"); } Class<?> proxyClass = null; /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; // for detecting duplicates Set<Class<?>> interfaceSet = new HashSet<>(); //验证interfaces里的接口是否能被类加载器加载,是否是接口,是否有重复的 for (int i = 0; i < interfaces.length; i++) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ String interfaceName = interfaces[i].getName(); Class<?> interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } /* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */ List<String> key = Arrays.asList(interfaceNames); //使用interfaces列表作为key缓存在cache里,也就是实现了相同interfaces的代理类只会创建加载一次 /* * Find or create the proxy class cache for the class loader. */ Map<List<String>, Object> cache; synchronized (loaderToCache) { cache = loaderToCache.get(loader); if (cache == null) { cache = new HashMap<>(); loaderToCache.put(loader, cache); } /* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ } /* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */ //看看缓存里有没有,如果有就直接取出来然后return,否则判断根据pendingGenerationMarker判断是否有其它线程正在生成当前的代理类, 如果有则cache.wait()等待,如果没有则创建。 synchronized (cache) { /* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */ do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class<?>) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { // proxy class being generated: wait for it try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker); break; } } while (true); } //确认要生成的代理类所属的包,如果interfaces里所有接口都是public的,代理类所属包就是默认包; 如果有interface不是public,那么所有不是public的interface必须在一个包里否则报错。 try { String proxyPkg = null; // package to define proxy class in /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, proxyPkg = ""; // use the unnamed package } { /* * Choose a name for the proxy class to generate. */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成代理类的名字,proxyPkg是上面确定下来的代理类所在的包名,proxyClassNamePrefix是写死的字符串“$Proxy”, num是一个全局唯一的long型数字,从0开始累积,每次生成新的代理类就+1,从这里也能看出生成的动态代理类的数量不能超过Long.maxValue /* * Verify that the class loader hasn't already * defined a class with the chosen name. */ /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); //生成一个以proxyName为类名的,实现了Interfaces里所有接口的类的字节码 try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); //加载生成的类 } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ //创建成功,则将cache中该key的pendingGenerationMarker替换为实际的代理类的弱引用,否则也要清除pendingGenerationMarker标记; 不管是否成功,都要执行cache.notifyAll(),让其它要创建相同代理类并且执行了cache.wait()的线程恢复执行。 synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference<Class<?>>(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; //最后返回代理类Class }
この時点で、動的プロキシの Java ソース コードが解析されました。 Proxy. newProxyInstance(ClassLoaderloader,Class>[]interfaces,InvocationHandler h) メソッドは、次の操作を実行するだけです: 1. パラメーター インターフェイスと
inherits
Proxy 内のすべてのインターフェイスを実装するプロキシ クラスを生成します。セクション コードを作成し、パラメータで classLoader を使用してプロキシ クラスをロードします。 3. このプロキシ クラスのインスタンスを返します。構築したプロキシ クラスはインターフェイス内のすべてのインターフェイス (つまり、プログラムで渡された subject.getClass().getInterfaces()) を実装しているため、返されるプロキシ クラスは強力になる可能性があります。インターフェースで定義されたメソッドを呼び出すには、Subject タイプに変換します。 Proxy.newProxyInstance() によって返された subjectProxy を Subject 型に正常にキャストして、インターフェイスで定義されたメソッドを呼び出すことができることがわかりました。それでは、メソッドを呼び出した後、プロキシ クラスのインスタンスをどのように処理する必要がありますか?プロキシクラスのソースコードを見てみましょう。しかし、プロキシ クラスはプログラムによって動的に生成されたバイトコードによってロードされます。ソース コードを確認するにはどうすればよいでしょうか。問題はありません。System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true") を main メソッドに追加すると、生成されたプロキシ クラスの Class ファイルがローカルに保存されます。ディスクをコピーし、コンパイルしてエージェント クラスのソース コードを取得します:package common; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Test.Subject { private static Method m4; private static Method m1; private static Method m3; private static Method m0; private static Method m2; static { try { m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (Exception e) { throw new RuntimeException(e); } } public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final void sayHello() { try { this.h.invoke(this, m4, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void sayHi() { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } }
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
总结
动态代理相对于静态代理在使用上的优点主要是能够对一个对象的所有方法进行统一包装,而且后期被代理的类添加方法的时候动态代理类不需要改动。
缺点是要求被代理的类必须实现了接口,因为动态代理类在实现的时候继承了Proxy类,java不支持多继承,因此动态代理类只能根据接口来定义方法。
最后动态代理之所以叫做动态代理是因为java在实现动态代理的时候,动态代理类是在运行时动态生成和加载的,相对的,静态代理类和其他普通类一下,在类加载阶段就加载了。
以上がJava動的プロキシのサンプルコードを深く理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。
