初心者のためのJavaデザインパターンの注意事項 - プロキシパターンの動的プロキシ

 

動的プロキシとは何ですか?

プロキシ オブジェクト ( 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) であり、実質的な作業を行うわけではありません。インスタンスを生成するときは、実際の作業を引き継ぐハンドラーを提供する必要があります。

 

 

おすすめ

転載: blog.csdn.net/louis_lee7812/article/details/83803451