概念
プロキシパターンとは、他のオブジェクトにプロキシを提供して、このオブジェクトへのアクセスを制御することです。このタイプのデザインパターンは構造パターンに属します。
エージェンシーパターンには通常、次の3つの役割があります。
- 抽象テーマの役割:主な責任は、実際のテーマとエージェントの共通のインターフェイスメソッドを宣言することです。このクラスは、インターフェイスまたは抽象クラスにすることができます。
- 実際のテーマの役割:このクラスはエージェントクラスとも呼ばれ、エージェントによって表される実際のオブジェクトを定義し、実行システムの実際の論理ビジネスオブジェクトを担当します。
- エージェントのテーマ役割:も保持しているエージェントクラスとして知られているを参照する本当のテーマ役割を内部的にそれは完全に持っている、代理店パワーのための本当のテーマの役割を。クライアントは、プロキシオブジェクトのメソッドを呼び出し、プロキシオブジェクトのメソッドも呼び出します。
分類
プロキシモードは、静的プロキシと動的プロキシに分けられます
静的プロキシ
プロキシクラスのバイトコードファイルは、プログラムの実行前にすでに存在しており、プロキシクラスとデリゲートクラスの関係は実行前に確認されています。
1.抽象的なテーマの役割クラスを作成します
public interface Payment {
String doPqy(String uid);
}
2.実際のテーマキャラクタークラスを作成します
public class ThirdChannelPayment implements Payment {
public String doPqy(String uid) {
System.out.println(uid+"->支付成功");
return "success";
}
}
3.エージェントテーマロールクラスを作成します
public class StaticProxyDemo implements Payment{
private Payment pay;
public StaticProxyDemo(Payment payment) {
this.pay=payment;
}
public String doPqy(String uid) {
System.out.println(uid+"发起支付");
return pay.doPqy(uid);
}
}
4.テストクラス
public class ProxyDemo {
public static void main(String[] args) {
StaticProxyDemo pay=new StaticProxyDemo(new ThirdChannelPayment());
System.out.println(pay.doPqy("Tom"));
}
}
操作結果:
UMLクラス図は以下のとおりです。
不利益
プロキシオブジェクトインターフェイスは1種類のオブジェクトのみを提供します。新しいメソッドを追加する場合、すべてのプロキシクラスでこのメソッドを実装する必要があるため、コードのメンテナンスコストが増加します。
動的プロキシ
動的プロキシクラスのソースコードは、プログラムの実行中にJVMリフレクションやその他のメカニズムによって動的に生成され、プロキシクラスとデリゲートクラスの関係は実行時に確認されます。
JDK動的プロキシ
jdkによって生成された動的プロキシを使用する前提は、ターゲットクラスに実装されたインターフェイスが必要であるということです。
1.動的プロキシクラス
public class DynamicProxy implements InvocationHandler {
private Object target;//被代理的对象
public Object bind(Object target){
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始");
Object result=method.invoke(target,args);
System.out.println("结束");
return result;
}
}
2.テスト
public class ProxyDemo {
public static void main(String[] args) {
//jdk动态代理
DynamicProxy dp=new DynamicProxy();
ThirdChannelPayment pay=new ThirdChannelPayment();
Payment p=(Payment) dp.bind(pay);
System.out.println(p.doPqy("Jam"));
}
}
動作結果:
CGLIBエージェント
最終的に変更されたクラスは継承できないため、使用の前提はターゲットをファイナライズできないことです。これは、動的に生成されたサブクラスがターゲットを継承する方法で実装されます。
プロキシクラス
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(Object target){
this.target=target;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始啦");
Object obj=methodProxy.invokeSuper(o,objects);
System.out.println("付款结束");
return obj;
}
}
テスト
public class ProxyDemo {
public static void main(String[] args) {
//cglib代理
CglibProxy cp=new CglibProxy();
ThirdChannelPayment tp=new ThirdChannelPayment();
Payment pay=(Payment) cp.getInstance(tp);
System.out.println(pay.doPqy("Alia"));
}
}
動作結果:
JDK動的プロキシとCGLIBプロキシの違い
- JDK動的プロキシはプロキシオブジェクトのインターフェイスを実装し、CGLIBプロキシはプロキシオブジェクトを継承します
- JDK動的プロキシとCGLIBプロキシはどちらも実行時にバイトコードを生成します。JDK動的プロキシはクラスバイトコードを直接書き込み、CGLIBプロキシはASMフレームワークを使用してクラスバイトコードを書き込みます。CGLIBプロキシの実装はより複雑であり、プロキシクラスの生成はJDK動的プロキシよりも効率的ではありません。
- JDK動的プロキシはリフレクションメカニズムを介してプロキシメソッドを呼び出し、CGLIBプロキシはFastClassメカニズムを介してメソッドを直接呼び出し、CGLIBプロキシの実行効率が高くなります
- Springフレームワークでは、Beanに実装インターフェイスがある場合、SringはJDK動的プロキシを使用します。Beanがインターフェイスを実装しない場合、SpringはCGLIBプロキシを選択します。
静的プロキシと動的プロキシの違い
- 静的エージェントは手動でのみエージェント操作を完了できます。エージェントクラスがメソッドを追加する場合、エージェントクラスを同期的に追加する必要があり、これは開閉の原則に違反します。
- 動的プロキシは、実行時にコードを動的に生成する方法を採用し、プロキシクラスの拡張制限を解除し、開始と終了の原則に従います。
開閉の原則:クラス、モジュール、関数などのソフトウェアエンティティは、拡張のために開いて、変更のために閉じる必要があります。
総括する
動的エージェントがターゲットクラスの拡張ロジックを拡張したい場合は、戦略モードと組み合わせることができ、エージェントクラスのコードを変更せずに新しい戦略クラスを追加することで完了できます。
プロキシモデルの利点:
- プロキシモードは、プロキシオブジェクトを実際の呼び出されたターゲットオブジェクトから分離し、システムの結合をある程度減らし、優れたスケーラビリティを備えています。
- ターゲットを保護する役割を果たすことができます
- ターゲットオブジェクトの機能を強化できます
プロキシモードのデメリット:
- エージェントモードは、システム設計のクラス数を増やし、システムの複雑さを増します
- クライアントとターゲットオブジェクトにプロキシオブジェクトを追加すると、リクエストの処理速度が低下します