Javaの動的なプロキシが実際には、このようなシーンのほとんど春AOP、アノテーション取得、ログ、ユーザー認証などとして使用シナリオの広い範囲を持っています。この記事では、プロキシモード、ネイティブJDKのダイナミックプロキシに基づいて静的剤で見てください。
プロキシモード
静的または動的プロキシエージェントを学んでいるかどうか、我々は最初のプロキシモードで見なければなりません。
Baiduの百科事典の定義を見てください:
プロキシモードを定義します。他のオブジェクトへのオブジェクトへのアクセスを制御するためにプロキシを提供します。いくつかのケースでは、オブジェクトが適切でないか、または直接、別のオブジェクトを参照しない、プロキシオブジェクトは、クライアントとターゲットオーディエンスの間の仲介の役割を果たすことができます。
直接の定義は、我々が説明するために具体的な例で生きている、理解することがやや困難に見えることがあります。
私たちは、製造業者から商品を購入するスーパーマーケットに販売した後、我々は通常、それはスーパーマーケットに渡し、そこから流れてどのように多くの商品がわからない、スーパーマーケット購入したアイテムになっています。
プロセスでは、私たちのためにそれは、製造業者(実際のオブジェクト)が表示されていないで、商品を販売するメーカー「委員会」スーパーマーケットに等しいです。私たちと対話する「ブローカー」のメーカーとしてスーパーマーケット(代理オブジェクト)が、。
一方、スーパーマーケットはまた、プロキシオブジェクトの機能を豊かにする販売プロセスに応じて割引することができます。
プロキシモードを通じ、我々は2つのことを行うことができます。
1、非表示の実装の委譲クラス。
2、デリゲートクラスのコードを変更せずに、などいくつかの追加機能(ログ、アクセス権)を、追加、クライアントとデリゲートクラスを切り離します。
プロキシモードでは、役割が定義されています
プログラミングプロセスの過程で、我々は3つのタイプのオブジェクトとして定義することができます。
- プロキシクラスのテーマだけでなく、プロキシプロキシクラスメソッドの本当のテーマを定義するための共通外交・真方法:件名(抽象的なテーマの役割)。たとえば、次のように宣伝、販売、上のようにします。
- RealSubject(本当のテーマ別の役割):実際のビジネス・ロジック・クラス。このような方法を実装メーカーの宣伝、販売、など(ベンダー)。
- プロキシ(代理テーマ別の役割):パッケージと現実のテーマのために行動します。たとえば、タイムアウトメソッドは、同じ広告、販売など(ショップ)を実現しています。
次のように、上記3つの役割のクラス図に対応しています。
静的プロキシインスタンス
静的プロキシは、プログラムが実行される前に、この場合は、プロキシクラスは、我々は通常のJavaコードで定義されて存在していたことをプロキシクラスです。
ここでは、あなたの静的なプロキシを表示するために、具体的な例を持っています。
まず、広告やマーケティング機能を提供するために使用される、インターフェイスのセットを販売して定義します。ベンダクラスは、次にインタフェース売り実装(スーパーマーケット、プロキシクラス)、(製造業者、代理オブジェクト)とショップが設けられています。
マーケットプレイスに次のようにインタフェースが定義されています。
/**
* 委托类和代理类都实现了Sell接口
* @author sec
* @version 1.0
* @date 2020/3/21 9:30 AM
**/
public interface Sell {
/**
* 出售
*/
void sell();
/**
* 广告
*/
void ad();
}
次のようにベンダークラスが定義されています。
/**
* 供应商
* @author sec
* @version 1.0
* @date 2020/3/21 9:30 AM
**/
public class Vendor implements Sell{
@Override
public void sell() {
System.out.println("Shop sell goods");
}
@Override
public void ad() {
System.out.println("Shop advert goods");
}
}
次のようにショップクラスが定義されています。
/**
* 超市,代理类
* @author sec
* @version 1.0
* @date 2020/3/21 9:30 AM
**/
public class Shop implements Sell{
private Sell sell;
public Shop(Sell sell){
this.sell = sell;
}
@Override
public void sell() {
System.out.println("代理类Shop,处理sell");
sell.sell();
}
@Override
public void ad() {
System.out.println("代理类Shop,处理ad");
sell.ad();
}
}
買い物前記参照ベンダプロキシクラスの重合方法及び対応する方法ベンダー対応するメソッド呼び出しによって保持されたプロキシクラス。ショップクラスでは、我々は、ユーザの購入、伐採や他の操作をフィルタリングして、いくつかの追加の処理を追加することができます。
クライアントにプロキシクラスを使用する方法で見てみましょう。
/**
* 静态代理类测试方法
* @author sec
* @version 1.0
* @date 2020/3/21 9:33 AM
**/
public class StaticProxy {
public static void main(String[] args) {
// 供应商---被代理类
Vendor vendor = new Vendor();
// 创建供应商的代理类Shop
Sell sell = new Shop(vendor);
// 客户端使用时面向的是代理类Shop。
sell.ad();
sell.sell();
}
}
上記のコードでは、顧客は販売インタフェースが機能を提供し、ターンショップで提供される機能を参照します。私たちは、プロキシクラスのベンダーに影響を与えることなく、ショップで何かを変更または追加することができます。
帯電防止剤の欠点
静的プロキシシンプルで元のコードが、時にシーンの複雑さに侵入しない、静的なエージェントは、次のような欠点があります:
複数のクラスは、ターゲット・オブジェクトのインタフェースと一致するように、プロキシオブジェクトがプロキシを必要1、。どちらかが複数のインタフェースを実装するためにのみ、プロキシクラスを維持しますが、これは、プロキシクラスになります大きすぎます。いずれかの新しい複数のプロキシクラスが、これは過度のプロキシクラスになることができます。
2、追加、削除、方法を変更するためのインタフェースの必要性は、ターゲット・オブジェクトもプロキシクラスを変更しなければならない時に、簡単に維持します。
だから、彼は便利なダイナミックプロキシに送信されます。
動的プロキシ
ダイナミックプロキシは、プログラム内のプロキシ方法を作成するためのプロキシクラスが実行されています。この場合、プロキシクラスはJavaコードで定義されていない、むしろJavaコードに基づいて動的に生成された実行時「を示します」。
帯電防止剤に比べて、エージェントは、各エージェントクラスの機能を変更することなく、プロキシクラス統一プロセスの機能に非常に便利であることができるダイナミックの利点を有します。
ネイティブJDKの動的プロキシ実装に基づいて
JDKのダイナミックプロキシとネイティブCGLIBのダイナミックプロキシ:動的剤は、通常2つの方法があります。ここでは、例として、ネイティブJDKのダイナミックプロキシに説明します。
java.lang.reflect.Proxyのとjava.lang.reflect.InvocationHandler:JDKのダイナミックプロキシは、2つの主なカテゴリが含まれます。
InvocationHandlerインタフェースは、次のメソッドを定義します。
/**
* 调用处理程序
*/
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
名前が示すように、代理店はとして使用するインタフェースクラス実装「コール・プロセッサを。」この方法は、エージェントクラスオブジェクト、メソッドを呼び出すために転送され、この「コール」と呼ばれている場合、パラメータ方式を識別渡された代理パラメータとしてプロキシクラスオブジェクトの特定のメソッド呼び出したプロキシクラス、メソッドの引数パラメータ。プロキシクラスのメソッドのすべてが呼び出しへの呼び出しになり、あなたはメソッドInvoke統一処理ロジックを追加することができため、このような呼び出しは、(あなたはまた、別のプロキシクラスのメソッドメソッドのパラメータに異なる処理を行うことができます)です。
プロキシクラスは、指定されたプロキシオブジェクトに関連付けられた呼処理を得るために使用されます。
あなたの動的プロキシを示すための例として、ログを追加するには、以下の。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
public class LogHandler implements InvocationHandler {
Object target; // 被代理的对象,实际的方法执行者
public LogHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args); // 调用 target 的 method 方法
after();
return result; // 返回方法的执行结果
}
// 调用invoke方法之前执行
private void before() {
System.out.println(String.format("log start time [%s] ", new Date()));
}
// 调用invoke方法之后执行
private void after() {
System.out.println(String.format("log end time [%s] ", new Date()));
}
}
次のようにクライアントは、動的プログラミングのプロキシコードを使用しています。
import java.lang.reflect.Proxy;
/**
* 动态代理测试
*
* @author sec
* @version 1.0
* @date 2020/3/21 10:40 AM
**/
public class DynamicProxyMain {
public static void main(String[] args) {
// 创建中介类实例
LogHandler logHandler = new LogHandler(new Vendor());
// 设置该变量可以保存动态代理类,默认名称$Proxy0.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 获取代理类实例Sell
Sell sell = (Sell) (Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[]{Sell.class}, logHandler));
// 通过代理类对象调用代理类方法,实际上会转到invoke方法调用
sell.sell();
sell.ad();
}
}
次のように実行した後、印刷ログは、次のとおりです。
调用方法sell之【前】的日志处理
Shop sell goods
调用方法sell之【后】的日志处理
调用方法ad之【前】的日志处理
Shop advert goods
调用方法ad之【后】的日志处理
検証の後、私たちは、プロキシクラスの団結を実行方法と実行方法の前と後にログに追加されたために成功していることがわかりました。
コード生成された動的プロキシクラスを見て上記の例では、我々は(本番環境で削除されるために、このプロパティの必要性)次のプロパティを追加します。
// 设置该变量可以保存动态代理类,默认名称$Proxy0.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
まあ、我々は主要な方法でも$ Proxy0.classクラスファイルの名前を生成し実行することができます。次のように逆コンパイルコードを見ることができます。
package com.sun.proxy;
import com.choupangxia.proxy.Sell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Sell {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void ad() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sell() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.choupangxia.proxy.Sell").getMethod("ad");
m3 = Class.forName("com.choupangxia.proxy.Sell").getMethod("sell");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我々は$ Proxy0(プロキシクラスは)Proxyクラスと実装するプロキシだけでなく、イコール、のhashCode、toStringメソッドによって、すべてのインターフェイスを継承見ることができます。
動的プロキシクラスに起因するプロキシクラスを継承し、それは、プロキシクラスのプロセッサのInvocationHandler各メソッド呼び出しに関連付けられます。
すべてのクラスとメソッドは、公開最終修正され、エージェントクラスのみを使用することができ、もはや継承することはできません。
方法を説明することを目的としている各方法は、メソッド静的オブジェクトは、「M +数値」形式名前、コードの静的ブロックが作成されます。
場合super.h.invokeによって呼び出す方法(この、M1、(オブジェクト[])NULL);コール。実際LogHandlerに渡されるSuper.h.invoke代理店の作成時にたとえば、Proxy.newProxyInstanceオブジェクト、それが実際の呼処理ロジックを担当するのInvocationHandlerクラスを継承します。
概要
ダイナミックプロキシエージェントと関連するコンテンツについては、我々はあまり話しています。プロキシモードでは、私たちのシステムをよりスケーラブルな設計を行うことができます学びます。広い、フレームワークと使用中のビジネスシナリオの様々なタイプの動的プロキシ・アプリケーション。2つの塩基を使用すると、より良い他のフレームワークを学ぶことができます。
動的なコンテンツCGLIBエージェントについては、我々は再び次の記事をチャット。
オリジナルリンク:「Javaプロキシモードと詳細動的な代理店」
「春のブート2.xのビデオチュートリアルの家族のバケット」、ブティック春ブーツ2.xのビデオチュートリアルは、春ブーツ2.xのビデオチュートリアルの中で最も完全なセットを作成します。