プロキシとは
たとえば
、世界中から学生を募集できるアメリカの大学があります。ただし、保護者の方は直接学校に行くことはできません。保護者が直接学校に行くことはできません。つまり、アメリカの学校は個人的な訪問を受け付けていません。現時点では、アメリカの学校を支援するために留学機関が必要です。リクルート。
仲介者は学校の代理人です。仲介者と学校は同じことをしなければなりません:学生を募集します。保護者にとっては学校が目標であり、留学機関が代理人です。日常生活の中で、エージェントの例はたくさんあります。たとえば、購入エージェント、不動産仲介業者、さまざまな仲介業者、IP取引所、商人、製造業者、購入者などです。開発中
同じ状況があります。たとえば、クラスがある場合、最初にクラスcのメソッドを呼び出して、特定の関数を完了します。しかし、cは呼び出しを許可しません。a ----- cのメソッドを呼び出すことはできません。aとcの間に直接bプロキシを作成し、cがbにアクセスできるようにします。a-訪問b ---訪問c。
元の訪問関係
プロキシを介したアクセス関係
プロキシモデルとは
Baidu百科事典
プロキシモードとは、他のオブジェクトにプロキシを提供して、このオブジェクトへのアクセスを制御することです。場合によっては、オブジェクトが適切でないか、別のオブジェクトを直接参照できず、プロキシオブジェクトがクライアントクラスとターゲットオブジェクトの間の中間的な役割を果たすことができます。
言い換えると、プロキシオブジェクトの使用は、ターゲットオブジェクトを変更せずにメインのビジネスロジックを強化することです。クライアントクラスが実際にアクセスしたいオブジェクトはターゲットオブジェクトですが、クライアントクラスが実際にアクセスできるオブジェクトはプロキシオブジェクトです。ターゲットオブジェクトへのクライアントのアクセスは、プロキシオブジェクトにアクセスすることによって実現されます
の。もちろん、プロキシクラスとターゲットクラスは同じインターフェイスを実装する必要があります。
エージェントを実装する方法
静的プロキシ
静的プロキシとは、プログラムが実行される前にプロキシクラスが定義されていることを意味します。Javaソースファイル。プログラムが実行される前に、ターゲットクラスとの関係が確立されています。プロキシクラスは、プログラムが実行される前に.classファイルにコンパイルされています。
静的プロキシの例を挙げてください
要件:ユーザーはUSBフラッシュドライブを購入する必要があります。USBフラッシュドライブは個別に購入することはできません。メーカーは、一度に少なくとも1,000台を購入できると規定しています。ユーザーはTaobaoエージェントまたはWechat販売店を通じて購入できます。TaobaoとWechatの製品はすべて、USBフラッシュドライブ工場の代理店であり、USBフラッシュドライブの販売代理店として機能します。
事件、出来事。ユーザーの購入-------エージェント(淘宝網、WeChatビジネス)----- uディスクメーカー(Kingston、SanDisk、その他のさまざまなメーカー)
1.ビジネスインターフェイスを定義します
抽象メソッドsell(int amout)を含むビジネスインターフェイスUsbSell(ターゲットインターフェイス)を定義します。sellはターゲットメソッドです。
public interface UsbSell {
/**
* 表示功能的,厂家和商家都要完成的功能
* @param amount
* @return
*/
float sellUsb(int amount);
}
2.インターフェースの実装クラスを定義します
ターゲットクラスUsbKingFactoryKingston USBフラッシュドライブ、このクラスはインターフェイスを実装します
import school.xauat.service.UsbSell;
public class UsbKingFactory implements UsbSell {
@Override
/**
* 定义金士顿厂家的销售价格
*/
public float sellUsb(int account) {
return 75.0f;
}
}
3.エージェントを定義します
TaoBaoは、メーカーの代理店としてUディスクを販売する代理店です。
import school.xauat.factory.UsbKingFactory;
import school.xauat.service.UsbSell;
public class Taobao implements UsbSell {
//声明 商家代理的厂家具体是哪一家
private UsbSell factory=new UsbKingFactory();
@Override
/**
* 实现销售U盘的功能
*/
public float sellUsb(int account) {
float price=factory.sellUsb(account);
//代理增强功能
price+=25;
return price;
}
}
WeiShangは、Uディスクを販売する代理店エージェントメーカーでもあります。
import school.xauat.factory.UsbKingFactory;
import school.xauat.service.UsbSell;
public class Weishang implements UsbSell {
//声明 商家代理的厂家具体是哪一家
private UsbSell factory=new UsbKingFactory();
@Override
public float sellUsb(int amount) {
float price=factory.sellUsb(amount);
//代理增强功能
price+=15;
return price;
}
}
4.クライアントの発信者、製品カテゴリの購入
クライアントは、TaobaoおよびWeiShangエージェントを介してUSBフラッシュドライブを購入できます
import school.xauat.business.Taobao;
import school.xauat.business.Weishang;
public class ShopMain {
public static void main(String[] args) {
Taobao taoBao=new Taobao();
float price=taoBao.sellUsb(1);
System.out.println(price);
Weishang weishang=new Weishang();
float price2=weishang.sellUsb(1);
System.out.println(price2);
}
}
上記のプロセスに従って、静的エージェントの長所と短所を分析します
利点:実装が簡単で理解しやすい
短所:
コードは複雑で管理が難しい
プロキシクラスとターゲットクラスは同じインターフェイスを実装し、各プロキシはターゲットクラスのメソッドを実装する必要があるため、コードの重複が多くなります。インターフェイスがメソッドを追加する場合、このメソッドを実装する必要があるすべてのターゲットクラスに加えて、すべてのプロキシクラスもこのメソッドを実装する必要があります。コードメンテナンスの複雑さが増しました
程度。
プロキシクラスはターゲットクラスに依存しており、プロキシクラスが多すぎます
複数のタイプを提供する場合、プロキシクラスは1つのタイプのターゲットクラスのみを提供します。プログラムの規模が少し大きく、プロキシクラスが多すぎると、静的プロキシは機能しなくなります。
動的プロキシ
動的プロキシとは、プログラムの実行時にリフレクションメカニズムに従ってJVMによって動的に生成されるプロキシクラスオブジェクトを指します。動的プロキシは、プロキシクラスの.javaソースファイルを定義する必要はありません。
動的プロキシは、実際にはクラスバイトコードを動的に作成し、jdkの実行中にJVMにロードします。
動的プロキシの一般的に使用される実装には、JDK動的プロキシ(主にここ)の使用とCGLIB動的プロキシの使用の2つがあります。
CGLIBエージェント
CGLIB(コード生成ライブラリ)はオープンソースプロジェクトです。これは強力で高性能、高品質のコード生成ライブラリであり、実行時にJavaクラスを拡張してJavaインターフェイスを実装できます。SpringAOPなどの多くのAOPフレームワークで広く使用されています。JDKを使用する
プロキシはプロキシを実装し、ターゲットクラスとプロキシクラスが同じインターフェイスを実装する必要があります。ターゲットクラスにインターフェイスがない場合、この方法で実装することはできません。ただし、インターフェイスのないクラスの場合、その動的プロキシを作成するには、CGLIBを使用して実現する必要があります。CGLIBプロキシ生成の原則は、ターゲットクラスのサブクラスを生成することです。
サブクラスが拡張され、サブクラスオブジェクトがプロキシオブジェクトになります。したがって、CGLIBを使用して動的プロキシを生成するには、ターゲットクラスを継承できる必要があります。つまり、最終クラスにすることはできません。Cglibは、Spring、Hibernateなどのフレームワークでよく使用されます。Cglibのプロキシ効率はJdkのプロキシ効率よりも高くなっています。正しい
cglibの一般的な開発では使用されません。ただ理解してください。
JDKプロキシ
jdk動的プロキシは、Javaのリフレクションメカニズムに基づいて実装されています。jdkのインターフェースとクラスを使用して、プロキシオブジェクトの動的な作成を実現します。Jdkのダイナミクスでは、ターゲットオブジェクトがインターフェイスを実装する必要があります。これはJava設計の要件です。jdk1.3以降、Java言語はjava.lang.reflectパッケージを介して3つのクラスを提供します
プロキシモードProxy、Method、InovcationHandlerをサポートします。
InvocationHandlerインターフェース
InvocationHandlerインターフェースは、呼び出しハンドラーと呼ばれ、ターゲットメソッドの呼び出しと関数の拡張を担当します。ターゲットインターフェイスのメソッドはプロキシオブジェクトを介して実行され、メソッドの呼び出しは呼び出しハンドラーの実装クラス(InvocationHandler)にディスパッチされ、実装クラスのiが実行されます。
nvoke()メソッドの場合、invoke()メソッドに関数プロキシを記述する必要があります。
ターゲットメソッドへの呼び出しは、invokeメソッドでインターセプトできます。ここで機能が強化されています。Javaの動的プロキシは、リフレクションメカニズムに基づいて構築されています。InvocationHandlerインターフェイスを実装するクラスは、ターゲットクラスのメインビジネスロジックを強化するために使用されます。このインターフェイスにはメソッドinvoke()があり、具体的には追加します
このメソッドでは、強力なコードロジックが定義されています。インターフェイスのメソッドがプロキシオブジェクトを介して実行されると、invoke()メソッドが自動的に呼び出されます。
invoke()メソッドの紹介は次のとおりです。
public Object invoke(オブジェクトプロキシ、メソッドメソッド、Object [] args)
プロキシ:生成されたプロキシオブジェクトを表します
method:ターゲットメソッドを表します
args:ターゲットメソッドのパラメータを表します
最初のパラメータプロキシは実行時にjdkによって割り当てられ、メソッドで直接使用されます。2番目のパラメータは後で導入され、3番目のパラメータはメソッド実行用のパラメータです。これらの3つのパラメータは実行時にjdkによって割り当てられ、プログラムは必要ありません。 。メンバーが与える。
メソッドクラス
invoke()メソッドの2番目のパラメーターはMethodクラスオブジェクトです。このクラスには、ターゲットメソッドを呼び出すことができるinvoke()とも呼ばれるメソッドがあります。2つのinvoke()メソッドの名前は同じですが、互いに関係はありません。
public Object invoke(Object obj、Object ... args)
obj:ターゲットオブジェクトを表します
args:上位レベルのinvokeメソッドの3番目のパラメーターであるターゲットメソッドパラメーターを表します
このメソッドの機能は、objオブジェクトが属するクラスのメソッドを呼び出すことであり、このメソッドは、その呼び出し元のメソッドオブジェクトによって決定されます。
コードでは、一般的な記述方法は次のとおりです。
method.invoke(target、args);
その中で、methodは前のレベルのinvokeメソッドの2番目のパラメーターです。このようにして、ターゲットクラスのターゲットメソッドを呼び出すことができます。
プロキシクラス
動的プロキシは、JDKのjava.lang.reflect.Proxyクラスを介して実装され、その静的メソッドnewProxyInstance()を使用して、ターゲットオブジェクト、ビジネスインターフェイス、および呼び出しプロセッサに基づいて動的プロキシオブジェクトが自動的に生成されます。
public static newProxyInstance(ClassLoaderローダー、Class <?> []インターフェース、InvocationHandlerハンドラー)
ローダー:ターゲットクラスのクラスローダー。ターゲットオブジェクトのリフレクションを通じて取得できます。
interfaces:ターゲットクラスによって実装されたインターフェイスの配列。ターゲットオブジェクトのリフレクションを通じて取得できます。
ハンドラー:ハンドラーを呼び出します。
jdk動的プロキシの実装手順
jdk動的プロキシはプロキシモードの実装であり、インターフェイスのみをプロキシできます。
実装手順
1.ターゲットインターフェイスとして新しいインターフェイスを作成します
2.ターゲットクラスであるインターフェイスの実装クラスを作成します
3. java.lang.reflect.InvocationHandlerインターフェースを実装するクラスを作成し、ターゲット・メソッドを呼び出し、他の関数コードを追加します
4.動的プロキシオブジェクトを作成し、Proxy.newProxyInstance()メソッドを使用して、戻り値をインターフェイスタイプに強制します。
例えば
1.ターゲットインターフェイスを作成し、ターゲットインターフェイスの機能を定義します
2.インターフェイスの実装クラスを作成します
上記の2つの手順は、静的プロキシと同じです。
3. InvocationHandlerインターフェイスの実装クラスを作成し、ターゲットメソッドを呼び出し、他のコード関数を追加します
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MySellHandler implements InvocationHandler {
//目标对象
private Object target=null;
public MySellHandler(Object target){
this.target=target;
}
@Override
/**
* 实现InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
* -调用目标方法
* -功能增强
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
result=method.invoke(target,args);
//功能增强
//这里为了简化,我们将功能增强定义为加价25元
if (result!=null){
float price=(float)result;
price=price+25;
result=price;
}
return result;
}
}
ここでのターゲットオブジェクトは、静的プロキシのTaoBaoおよびWeiShangと同等です。
4. Uディスクを購入する顧客をシミュレートし、proxy.newProxyInstanceを使用してプロキシプロキシオブジェクトを作成し、戻り値をターゲットインターフェイスタイプにします
import school.xauat.factory.UsbKingFactory;
import school.xauat.handler.MySellHandler;
import school.xauat.service.UsbSell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) throws Exception {
//创建目标对象
Class c=UsbKingFactory.class;
Object obj=c.newInstance();
//获得目标类的类加载器
ClassLoader loader =UsbKingFactory.class.getClassLoader();
//获取目标类实现的接口数组
Class<?>[]interfaces=obj.getClass().getInterfaces();
//创建InvocationHandler对象
InvocationHandler handler=new MySellHandler(obj);
//创建代理对象
UsbSell proxy=(UsbSell) Proxy.newProxyInstance(loader,interfaces,handler);
//通过这个代理执行方法
float price=proxy.sell(1);
System.out.println(price);
}
}
静的プロキシ
動的プロキシ
UML图