動的プロキシとは何ですか?
プロキシ オブジェクト ( RealSubject ) は実行時に動的に変更でき、制御する必要があるインターフェイス ( Abstract Subject (Subject) ) も実行時に変更でき、制御メソッドも動的に変更できるため、非常に柔軟な動的エージェンシーを実現できます。関係。
Java は動的プロキシの実装メソッドを提供します。
Java の java.lang.reflect ライブラリでは、動的プロキシを実装するために次の 3 つのクラスが提供されています。
プロキシ (クラス):このクラスはプロキシ設定を表し、通常はタイプ (http、socks) とソケット アドレスを表します。Proxy
は不変オブジェクトです。
InvocationHadler (インターフェイス):呼び出しハンドラーと呼ぶことができ、プロキシ インスタンスの呼び出しハンドラーによって実装されるインターフェイスです。各エージェント インスタンスには、関連付けられたコール ハンドラがあります。プロキシ インスタンスでメソッドが呼び出される場合、メソッド呼び出しはエンコードされ、その呼び出しハンドラーのメソッドにディスパッチされますinvoke
。
メソッド (クラス):クラスまたはインターフェイスの個々のメソッドに関する情報 (およびメソッドへのアクセス方法) を提供します。反映されるメソッドは、クラス メソッドまたはインスタンス メソッド (抽象メソッドを含む) です。
では、動的プロキシを実装するにはどうすればよいでしょうか? 主に以下の4つのステップを経ていきます。
1)抽象的なテーマを定義する
2)実際のトピックを定義する
最初の 2 つは静的プロキシと同じです。重要なのは、この動的なエージェントの役割をどのように実装するかです。
3) InvocationHadler (インターフェイス)を実装する必要があります。このインターフェイスの実装クラスは、動的プロキシ クラスの実際の作業を完了します。
このインターフェイスの invoke メソッドでは、 Java のリフレクション メカニズムを使用して、 Method (class)を通じて実際のテーマを動的に呼び出します。
4) クライアント (または InvocationHadler の実装クラス) で、Proxy (クラス)の静的メソッド newProxyInstance を呼び出すと、プロキシ オブジェクトが返されます(これは動的プロキシ クラスであり、実際の作業は実行しません)。
動的プロキシの実装メカニズムをコードを通して理解しましょう。
また、プロキシ (プロキシ) モードの静的プロキシで車を購入する例も考えてみましょう- 初心者のための Java デザイン パターンに関するメモ。
実生活では、販売者(抽象主題)から車を購入する場合、通常はメーカー(実際の主題)から直接車を購入することはありません。4Sストア(代理役割)から車を購入できます。詳細情報や多くのサービスを入手してください。
Java コードは次のとおりです。
抽象的な主題:
/**
* 抽象主题(Subject)
*
* 汽车的销售商
*
*/
public interface CarSeller {
/*
* 销售汽车
*/
public Object sellCars(int type);
}
実際の主題:
/**
* 真实主题(Real Subject)角色
* 奥迪厂家
*
*/
public class AudiCarFactory implements CarSeller {
/*
* 实现了抽象主题(Subject)角色的方法
*/
public Object sellCars(int type) {
System.out.println("奥迪工厂出售汽车。");
if(type == 1){
return "AudiA6";
}else{
return "AudiA8";
}
}
}
呼び出しプロセッサ (InvocationHadler の実装クラス):
/*
* 实际提供代理商服务的一个类
* (很多地方把这个类称为代理类,个人觉得不准确。
* 它是一个会完成动态代理类的实际工作的类。)
*/
public class CarProxyInvocationHandler implements InvocationHandler {
Object carSeller = null;
public CarProxyInvocationHandler(Object carSeller){
this.carSeller = carSeller;
}
/*
* 代理角色提供服务的真正方法。
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args){
Object result = null;
try {
serveBeforeSell();
result = method.invoke(carSeller,args);
serveAfterSell();
} catch (Exception e) {
System.exit(1);
}
// System.out.println("result:" + result +";" + args[0]);
return result;
}
protected void serveBeforeSell(){
System.out.println("汽车代理商为客户提供了一些售前服务");
}
protected void serveAfterSell(){
System.out.println("汽车代理商为客户提供了一些售后服务");
}
}
クライアント:
/**
* 客户端调用
*/
public class Customer {
public static void main(String[] args) {
// 创建真实主题对象
CarSeller carSeller = new AudiCarFactory();
// 通过动态的方式,得到了真正的代理角色对象。
// 在运行时,真实主题对象,代理角色以及实际完成代理操作的类被关联起来了。
CarSeller carProxy = (CarSeller) Proxy.newProxyInstance(carSeller.getClass()
.getClassLoader(), carSeller.getClass().getInterfaces(),
new CarProxyInvocationHandler(carSeller));
// 由代理商销售汽车
Object car = carProxy.sellCars(2);
System.out.println("顾客从代理商那里买了一辆" + car);
}
}
操作結果:
汽车代理商为客户提供了一些售前服务
奥迪工厂出售汽车。
汽车代理商为客户提供了一些售后服务
顾客从代理商那里买了一辆AudiA8
Java ダイナミック プロキシの実装を初めて見ると、常に理解しにくいところがいくつかあります。
1) Proxy クラスの newProxyInstance メソッドの 3 つのパラメーターは何に使用されますか?
最初のパラメーターは、実際のテーマオブジェクトによって使用されるクラス ローダーです。-----作成された動的プロキシ クラスをロードするために使用されます。
2 番目のパラメーターは、実際のテーマオブジェクトによって実装されるすべてのインターフェイスです。-----動的プロキシ クラスの場合、実際のテーマのすべてのインターフェイスが実装されます。
3 番目のパラメーター、呼び出しハンドラーのインスタンス(InvocationHadler の実装クラス) -動的プロキシ クラスは Proxy クラスを継承するため、そのInvocationHadler 実装クラスを使用してサブクラス (通常は動的プロキシ クラス) から構築します。関数は新しいProxy
インスタンスを構築します。 。(jdk のヘルプドキュメントを参照してください)
このようにして、取得した動的プロキシ クラス $Proxy0 は実際のトピックのプロキシとして指定され、プロキシはProxy ( class)を継承し、抽象トピック インターフェイスも実装し、InvocationHadlerインターフェイスにバインドされます。
動的プロキシ クラス $Proxy0 は実行時に作成されるため、このクラスのコードを直接見ることはできません。詳細については、以下のリンクを参照してください。
Java動的プロキシ深層学習(Proxy、InvocationHandler)、$Proxy0ソースコードを含む(転送)
2) InvocationHadler インターフェースの invoke メソッドの 3 つのパラメーターは何に使用されますか。
最初のパラメータは動的プロキシ オブジェクト $Proxy0 ですが、通常は使用されません。ただし、このパラメーターのおかげで、このオブジェクトのメソッドにアクセスできます。
2 番目のパラメーターは、プロキシ オブジェクト (実際のサブジェクト)によって定義されたメソッドです。
3 番目のパラメータは、プロキシ オブジェクト (実際のサブジェクト) によって定義されたメソッドのパラメータです。
2 番目と 3 番目のパラメーターは、Method クラスのリフレクション メカニズムを使用して、テーマ オブジェクトを動的に呼び出すことができます。
3) 動的プロキシクラスのメソッドを呼び出す場合、InvocationHadler インターフェースの invoke メソッドを呼び出すにはどうすればよいですか?
動的プロキシ クラス $Proxy0のコード によれば、InvocationHadler の呼び出しメソッドが、それが実装する抽象テーマ インターフェイスのメソッドで呼び出されていることがわかります。そして、動的プロキシ クラス $Proxy0自体の this ポインターを invoke メソッドの最初のパラメーターとして使用します。
これら 3 つの点を理解し、上記のコードと組み合わせると、動的プロキシの動作メカニズムを理解できます。
1)抽象テーマ クラス (CarSeller)を継承する実際のテーマ (AudiCarFactory)オブジェクトを作成します。
2) Proxy クラスの newProxyInstance メソッドを使用して、動的プロキシ クラス $Proxy0を取得し、CarProxyInvocationHandler インスタンスをバインドします。
3)動的プロキシ クラス $Proxy0 はProxy クラスを継承し、実際の対象オブジェクト (この場合は CarSeller のみ) のすべてのインターフェイスを実装します。
4)動的プロキシ クラス $Proxy0によって実装された抽象テーマ (CarSeller)クラスのメソッド (sellCars) で、CarProxyInvocationHandler の invoke メソッドが呼び出されます。
5) CarProxyInvocationHandler の invoke メソッドで、実際のテーマ (AudiCarFactory)の sellCars メソッドが呼び出され、sellCars の前後に操作が追加されます。
タイミング図を以下に示します。より明確になるはずです。
動的プロキシ クラスは、実行時およびクラスの作成時に実装するインターフェイスを指定できるクラスです。プロキシ クラスの各インスタンスには、対応するInvocationHandlerオブジェクトがあります。
ネチズンの要約を引用すると、次のようになります。
いわゆる動的プロキシ (Dynamic Proxy) はクラスです: 実行時に生成されるクラスです。生成するときは、それに一連のインターフェイスを提供する必要があり、クラスはこのインターフェイスを実装していることをアドバタイズします。もちろん、このクラスのインスタンスをこのインターフェイスのいずれかとして使用できます。もちろん、この動的プロキシ (Dynamic Proxy) は実際にはプロキシ (Proxy) であり、実質的な作業を行うわけではありません。インスタンスを生成するときは、実際の作業を引き継ぐハンドラーを提供する必要があります。