動的プロキシプロキシモード-jdk
より多くの技術的のための第JavaStorm世間の注目。
オブジェクトへのクライアントアクセスを制御するために別のオブジェクトに代わって設けられています。どのように定義されます。別のオブジェクトにこのオブジェクトへのアクセスを提供するために、エイリアスまたはプレースホルダ。具体的には、あなたが見ることができgithub.com/UniqueDongを/ ...
プロキシは何ですか
マンダリン話すは希望そのコードの再利用におけるデザインパターンです。我々は異なるオブジェクトを訪問していた方法では、プロキシモードでは、ターゲットオブジェクトを介して、しかし、代理アクセス私たちのターゲットオーディエンスと方法を介して直接ではありません。時には我々は、対象物との直接接触を確立したりすることができないので、私たちは、クライアントのアクセスを制御する必要があります。そこで、彼らはプロキシを介して、当社の実際のオブジェクトを訪ねてきます。
>「スターブローカー」 - - >「スター。」「顧客」のように 私たちが直接スターとリンクされていない、星のは忙しく、歌うと価格には十分熱いヘッド映画、ダンスに、証券会社の担当者はスターが仕事を引き受けた通知しました。
主な役割
- 件名:テーマインタフェースは、プロキシクラスとプロキシクラスに実装されます。
- ConcreteSubject:プロキシクラス、また、実際のオブジェクトである委譲クラス、実際の作業として知られています。
- プロキシ:プロキシオブジェクトを生成するプロキシクラス、俳優の仲介、実際のオブジェクトに対する制御クライアントアクセスの役割は、クライアントの呼び出しに、我々は遮蔽することができるか、実際のオブジェクトのプロキシ処理により透明ことができます。
利用シナリオ
- より一般的なMyBatisのよう私たちはSQLがステートメントの実際の実装に移動し、その本質は、動的プロキシの使用である、マッパーを介したインターフェースの実際のロジックに呼び出しを実行することができ、内部の私たちの人気のあるオープンソースのフレームワークに表示されます。
- 春AOPは、実際に彼が強化されたトランザクション、コードの再利用を実現するために、私たちのトランザクション処理ロジックをカプセル化するプロキシクラスを生成します方法は、春のトランザクション管理によって呼び出される春の取引、などの動的プロキシの使用です。私たちは、トランザクションを提出するか、トランザクションをロールバックするトランザクション、実行ロジック、テンプレートコードの解放をオンにし、私達はちょうど路盤コードを書くために安心します。ここで、「テンプレートメソッドパターン」動的プロキシモードの合計を使用します。「テンプレートメソッド」パターンは、物品の歴史を参照することができます。
- そこダボRPCフレームワーク、動的プロキシを使用しています。消費者のために我々は唯一のローカル呼び出しと同じように、実現人々が提供するインタフェースを介して関数を呼び出すことができます。それは私たちのネットワーク伝送、直列化、暗号化復号の退屈な詳細をカプセル化します。プロキシクラスは、シールドの低レベルの詳細のために生成されます。それは私たちが透過的に呼び出すことができます。
我々は、プロキシクラスのタイミングに応じて異なる負荷、エージェントは、静的および動的プロキシエージェントに分割されます。私たちは、コードのコンパイル時に、プロキシクラスということを決定した場合は、直接静的プロキシを使用できる1;あなたがわからない場合は、動的ローディング機構は、コードが動的であるプロキシクラスをロードするために実行している間に、このクラスを使用することができますこのようなスプリングAOPフレームワークとRPC機構として剤。
静的プロキシコード例
我々は最初の件名インターフェイスを作成、単純な静的プロキシモードを実装するためにUMLのクラス図に従ってください。
public interface Subject {
public void request();
}
复制代码
このインタフェースを実装する実際のオブジェクトを作成するには
public class RealSubjbect implements Subject {
@Override
public void request() {
System.out.println("this is RealSubjbect.request()");
}
}
复制代码
件名インターフェースを実現しながら、実際のオブジェクトへの参照を保持し、私たちのプロキシクラスを作成します。
public class ProxySubject implements Subject {
private Subject realSubjbect = null;
/**
* 除了代理真实角色做该做的事,代理角色提供附加操作
* 如
*/
@Override
public void request() {
preRequest(); //真实角色操作前的附加操作
if (realSubjbect == null) {
realSubjbect = new RealSubjbect();
}
realSubjbect.request();
postRequest();//真实角色操作后的附加操作
}
/**
* 真实角色操作前的附加操作
*/
private void postRequest() {
System.out.println("真实角色操作后的附加操作");
}
/**
* 真实角色操作后的附加操作
*/
private void preRequest() {
System.out.println("真实角色操作前的附加操作");
}
}
复制代码
プロキシクラスに呼ばれる私たちのクライアント・インタフェースを、書きます。
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request(); //代理者代替真实者做事情
}
}
复制代码
結果はプリントの下に表示され、我々は、実際のオブジェクトの制御を実現し、いくつかの操作を追加します。同じの機能春AOPの実装と同様に。
真实角色操作前的附加操作
this is RealSubjbect.request()
真实角色操作后的附加操作
复制代码
短所
- プロキシ多くの方法にしたい場合は、インターフェイスプロキシオブジェクトは、オブジェクトの一種類のみを提供するために、各メソッドのエージェントを実行しているにバインドされ、プログラムの少し大きめのサイズの帯電防止剤は、有能実行することはできません。
- すべての実装クラスに加えて、メソッドを追加するインターフェイスはこのメソッドを実装する必要がある場合は、すべてのプロキシクラスも、このメソッドを実装する必要があります。これは、コードの保守の複雑さを増します。
あなたは上記の方法でプロキシモードを使用する場合はさらに、真の役割(代表者)は、すでに事前に存在し、プロキシオブジェクトの内部プロパティとしてしなければなりません。大量の使用は、クラスの急速な拡大につながることができます場合は、実際の使用では、役割は、実際の演技の役割に対応している必要があり、実際の役割(代理人)が、どのようにプロキシを使用する場合は、さらに、事前に知っていないのですか?この問題は、Javaの動的プロキシクラスによって解決することができます。
動的プロキシ
コンセプト
ダイナミックプロキシクラスJVM反射源を動的プログラム実行中に生成され、プロキシクラスのバイトコードファイルが存在しません。関係プロキシクラスと委譲クラスは、実行時に決定されます。
実装
主に2つの方法があります:
- JDKの実装を使用します。リバース委譲クラスはインターフェースを持っています。
- 使用CGLIBを達成。変更されたクラスにはパブリックメソッドのみ変更、最終的にはできません。
JDKダイナミックプロキシ
UMLクラス図を以下に示します。
- あなた自身の作成
Handler
を実現しInvocationHandler
、実際のオブジェクト参照を保持しながら、私たちのビジネスクラスに依存します。 - プロキシオブジェクトによって
Proxy.newProxyInstance()
生成され、そしてにおける方法のでれるHandler
物体と現実物体。
実際にはクラス図では、我々はまた、プロキシバイトコード生成の呼び出しによる動的プロキシがハンドラに委託されたときにメソッドを呼び出すことを知ることができます。一方、ハンドラとは、実際のビジネスオブジェクトを保持します。私たちは、実際のオブジェクトの呼び出しや訪問を実行する前に、自分の行動を制御することができます。主な利点:
- 実装隠れ委譲クラスは、呼び出し側は相互に作用し、プロキシクラスする必要があります。
- デカップリング、追加の処理が状況デリゲートクラスコードで変化しません。このようなダボクライアントの呼び出しでシリアライズ、ネットワーク伝送の詳細を追加するなど。
コードの実装
クラス図では、我々は、JDKの動的プロキシが3つの段階に分けることができます作成します。
- ビジネス・インターフェースを定義し、実装クラスは、エージェントです。
- 新しいカスタムのInvocationHandler実装する
InvocationHandler
インタフェースをし、依存している(実際のオブジェクトが参照)プロキシクラス。 - することで
Proxy.newProxyInstance()
、特定のプロキシオブジェクトのメソッドを作成します。
今、私たちは需要は、当社の統一されたビジネスロジックを記録するためにコールログ持っているか、我々はログレコードを記述し、トランザクション制御は、私たちのプロキシクラスのログです。春AOP同様のシミュレーション機能、例の簡易版、我々は彼らの利用シナリオや原理を理解するように。
私たちはあなた自身のビジネスインタフェースを定義します。最初のステップOrderService
でProductService
public interface OrderService {
String createOder(String no);
}
public interface ProductService {
String getProduct(String no);
}
复制代码
ビジネスロジックの実装クラス
public class OrderServiceImpl implements OrderService {
@Override
public String createOder(String no) {
return "生成订单" + no + "成功";
}
}
public class ProductServiceImpl implements ProductService {
@Override
public String getProduct(String no) {
return "获取商品" + no + "成功";
}
}
复制代码
ステップ2:私たちのハンドラは、JDKのInvocationHandlerを達成定義、手数料を基準に定義保持しますObject
あなたはすべてのログのニーズにプロキシクラスを委任することができますので、種類を、私たちは持っているbind()
と同時に、ターゲットを設定する方法ができるように、対応するプロキシクラスを返します。クライアントが呼び出します。たとえば、Proxy.newProxyInstance()メソッドの第3のステップは、我々は内側に結合します。コードより簡潔な。
public class LoggerInterceptor implements InvocationHandler {
/**
* 持有委托类的引用
*/
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println(proxy.getClass().getInterfaces()[0]);
//通过反射调用
System.out.println("Entered " + target.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
//调用目标对象的方法
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("执行结果:" + result.toString());
System.out.println("共耗时:" + (end - start));
return result;
}
/**
* 获取代理类:并将 target 委托类绑定到 我们定义的Targe中
* @param target 真实的委托类
* @return
*/
public synchronized Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
复制代码
最後に、我々は結果を確認するために、新しいカテゴリを開始し、2つのビジネス・ロジック・クラスは、ロギングを必要があります。特定のコードのコメントを参照してください。
public class BootStrap {
public static void main(String[] args) {
// 新建业务逻辑
OrderService orderService = new OrderServiceImpl();
ProductService productService = new ProductServiceImpl();
// 新建我们的 日志 Handler
LoggerInterceptor loggerInterceptor = new LoggerInterceptor();
//绑定 委托类同时生产代理类调用 。
OrderService orderServiceProxy = (OrderService) loggerInterceptor.bind(orderService);
orderServiceProxy.createOder("12927381");
ProductService productServiceProxy = (ProductService) loggerInterceptor.bind(productService);
productServiceProxy.getProduct("34010234");
}
}
复制代码
次のように印刷するには、次のとおりです。我々は両方の印刷ログの薬剤であり、その結果を返す方法を実行するために、各インターフェイスの前と後に見ることができます。実際には、春AOPは、動的プロキシを介して達成されます。
nterface com.zero.headfirst.proxy.service.OrderService
Entered com.zero.headfirst.proxy.service.OrderServiceImpl-createOder,with arguments{12927381}
执行结果:生成订单12927381成功
共耗时:2
interface com.zero.headfirst.proxy.service.ProductService
Entered com.zero.headfirst.proxy.service.ProductServiceImpl-getProduct,with arguments{34010234}
执行结果:获取商品34010234成功
共耗时:1
复制代码
CGLIB動的プロキシ
CGLIBは、強力な、高性能なコード生成パッケージです。これは、広くそのような彼らのために設けられたスプリングAOP傍受法(傍受)のような多くのAOPフレームワークによって使用されます。CGLIB下のパッケージには、バイトコードを変換して、新しいクラスを生成するために、小型で高速なバイトコード処理フレームワークのASMを使用することです。
最終的なクラスは変更することができず、最終的な方法は、インタフェースが定義されなくてもよいです。
使用は簡単です:私たちは、最初のCGLIBの依存、MethodInterceptorのを実装する新しいクラスを追加する必要があります。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LoggerProxy implements MethodInterceptor {
/**
* 创建代理类
* @param targetClass 委托类
* @return
*/
public Object bind(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
//设置回调方,当客户端通过代理调用方法的时候会会调用我们重写的 intercept() 方法
enhancer.setCallback(this);
//创建代理类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
//通过反射调用
System.out.println("Entered " + o.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
Object result = methodProxy.invokeSuper(o, args);
long end = System.currentTimeMillis();
System.out.println("执行结果:" + result.toString());
System.out.println("共耗时:" + (end - start));
return result;
}
}
复制代码
それは単純ではないですか?新エンハンサー、限り委譲クラスと設定したコールバッククラスなど。ここでは、工場出荷時のパターンを使用してプロキシ対応するコールバックの異なる種類を作成することができます。ここでは簡単な例では書いていません。
次に、我々のテスト結果とクラスを見て
public class CglibBootStrap {
public static void main(String[] args) {
LoggerProxy loggerProxy = new LoggerProxy();
OrderService orderService = (OrderService) loggerProxy.bind(OrderServiceImpl.class);
orderService.createOder("12873051209g");
ProductService productProxy = (ProductService) loggerProxy.bind(ProductServiceImpl.class);
productProxy.getProduct("2780935782309");
}
}
复制代码
印刷結果を以下に示します
Entered com.zero.headfirst.proxy.service.OrderServiceImpl$$EnhancerByCGLIB$$54d983a1-createOder,with arguments{12873051209g}
执行结果:生成订单12873051209g成功
共耗时:14
Entered com.zero.headfirst.proxy.service.ProductServiceImpl$$EnhancerByCGLIB$$4e2c7c36-getProduct,with arguments{2780935782309}
执行结果:获取商品2780935782309成功
共耗时:5
复制代码