jdk 動的プロキシを使用してクラスをプロキシするには、プロキシされるクラスは少なくとも 1 つのインターフェイスを実装する必要があり、インターフェイス内のメソッドのみをプロキシできます。
jdk での動的プロキシの実装は、通常、次の 3 つのステップに分かれています。
1.インターフェイスと実装クラスを作成します。
2. InvocationHandler インターフェイスを実装するプロセッサを作成します。このインターフェイスにはメソッドが 1 つだけあり、そのシグネチャは public Object invoke(Object proxy, Method method, Object[] args) です。
throws Throwable; メソッド呼び出しの前後にプロセッサの実装メソッドに独自のコードを追加して、動的インターセプトを実行できます。プロキシによって生成された動的プロキシ クラスは、プロキシしている実際のクラスではないことに注意してください。そのため、実際にプロキシしたいクラス (つまり、ステップ 1 実装クラスのクラス)。
3. java.lang.reflect.Proxy クラスの newProxyInstance メソッドを使用して、動的プロキシ クラスを生成します。プロキシ メソッドへのすべての呼び出しでは、生成された動的プロキシ クラスのメソッドを直接呼び出すことができますが、最初に強制型変換を実行して、呼び出したいメソッドのインターフェイスに変換する必要があります。
JDK の原理分析:
Proxy のソース コードを分析すると、動的プロキシ クラスの詳細な生成を確認できます。 newProxyInstance メソッドは、最初に動的プロキシ クラスの Class インスタンスを生成し、次にパラメータの型が InvocationHandler であるコンストラクタを呼び出して動的プロキシ クラスを生成して戻ります。
動的プロキシ クラスの Class インスタンスを生成するにはどうすればよいですか? ProxyGenerator クラスを使用して、動的プロキシ クラスのクラス バイト ストリームを生成し、それをメソッド領域にロードします。
クラスのバイト ストリーム生成のプロセスを分析すると、プロキシされるインターフェイスのすべてのメソッドを実装するために Proxy を親クラスとして使用し、各メソッドの実装本体は主にプロセッサの invoke メソッドを呼び出していることがわかります。
クラスバイトストリーム生成プロセスのメインコードは次のとおりです。
次のようにコードをコピーします。
プライベート バイト[]generateClassFile()
{
addProxyMethod(hashCodeMethod, java/lang/Object);
addProxyMethod(equalsMethod, java/lang/Object);
addProxyMethod(toStringMethod, java/lang/Object);
for(int i = 0; i < インターフェイスの長さ; i++)
{
メソッド amethod[] = インターフェース[i].getMethods();
for(int k = 0; k < amethod.length; k++)
addProxyMethod(メソッド[k], インターフェイス[i]);
}
リストリスト;
for(イテレータ iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
list = (リスト)iterator.next();
試す
{
メソッド.add(generateConstructor());
for(イテレータ iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();)
{
リスト list1 = (リスト)iterator1.next();
イテレータ iterator2 = list1.iterator();
while(iterator2.hasNext())
{
ProxyMethod プロキシメソッド = (ProxyMethod)iterator2.next();
field.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
Methods.add(<SPAN style="COLOR: red">proxymethod.generateMethod()</SPAN><SPAN style="COLOR: #000000">);</SPAN>
次のようにコードをコピーします。
}
}
Methods.add(generateStaticInitializer());
}
catch(IOException io例外)
{
throw new InternalError("予期しない I/O 例外");
}
if(methods.size() > 65535)
throw new IllegalArgumentException("メソッドの制限を超えました");
if(フィールド.サイズ() > 65535)
throw new IllegalArgumentException("フィールド制限を超えました");
cp.getClass(dotToSlash(クラス名));
cp.getClass("java/lang/reflect/Proxy");
for(int j = 0; j < インターフェイスの長さ; j++)
cp.getClass(dotToSlash(interfaces[j].getName()));
cp.setReadOnly();
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
DataOutputStream dataoutputstream = 新しい DataOutputStream(bytearrayoutputstream);
試す
{
dataoutputstream.writeInt(-889275714);
dataoutputstream.writeShort(0);
dataoutputstream.writeShort(49);
cp.write(dataoutputstream);
dataoutputstream.writeShort(49);
dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
dataoutputstream.writeShort(interfaces.length);
for(int l = 0; l < インターフェイスの長さ; l++)
dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));
dataoutputstream.writeShort(fields.size());
フィールド情報フィールド情報;
次のようにコードをコピーします。
//属性を追加する
for(イテレータ iterator3 = field.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
フィールド情報 = (フィールド情報)iterator3.next();
//メソッドを追加
dataoutputstream.writeShort(methods.size());
MethodInfo メソッド情報;
for(イテレータ iterator4 = Methods.iterator(); iterator4.hasNext(); Methodinfo.write(dataoutputstream))
メソッド情報 = (メソッド情報)iterator4.next();
dataoutputstream.writeShort(0);
}
catch(IOException ioException1)
{
throw new InternalError("予期しない I/O 例外");
}
bytearrayoutputstream.toByteArray() を返します。
}
注:コード内の赤い部分、proxymethod.generateMethod() は、各メソッドのメソッド本体を生成します。ソース コードを見ると、それらはすべて InvocationHandler インターフェイスの実装プロセッサの invoke メソッドを呼び出していることがわかります。