静的プロキシ
プロキシモードの定義を開始する前に、一般的なビジネスロジックの一部を見てみましょう。インターフェイスISubjectがあり、インターフェイスに演算子メソッドがあり、このメソッドを実装するための特定の実装クラスがあるとします。
- インターフェイスクラス
public interface ISubject {
void operator();
}
- 特定の実装クラス
public class RealSubject implements ISubject{
@Override
public void operator() {
System.out.println("do something");
}
}
これで、新しい要件があります。つまり、演算子メソッドを実行する前にログを出力する必要がありますが、元のRealSubjectのビジネスロジックを変更することはできません。それを実現するにはどうすればよいですか?
現時点では、大物は間違いなくISubjectを実装してRealSubjectを組み合わせるための新しい実装クラスを構築することを考えます。実際のビジネスロジックは、RealSubjectの演算子メソッドを呼び出して実装し、演算子を実行する前に必要なビジネスロジックを実装することです。ログ印刷として。
public class SubjectProxy implements ISubject{
private RealSubject subject;
public SubjectProxy(RealSubject subject) {
this.subject = subject;
}
@Override
public void operator() {
System.out.println("this is log");
subject.operator();
}
}
クライアントが呼び出すとき、SubjectProxyを直接使用してビジネスロジックを実装できます。
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ISubject proxy = new SubjectProxy(realSubject);
proxy.operator();
}
}
結果は次のとおりです。
これを見て、これはプロキシモードだと思うかもしれません。これです?
はい、これはプロキシモードです。この例を理解した後、プロキシモードをほぼ習得しましたが、それでも静的プロキシです。動的プロキシを実装する方法については後で説明します。
まず、プロキシモデルの定義を見てみましょう。
エージェントモードの定義
プロキシモードのクラス図の構造は次のとおりです。
図のSubjectは、プログラムのビジネスロジックインターフェイス、RealSubjectは、Subjectインターフェイスを実装する実際のビジネスクラス、Proxyは、RealSubject参照をカプセル化するSubjectインターフェイスを実装するプロキシクラスです。RealSubjectオブジェクトのメソッドはプログラムで直接呼び出されませんが、Proxyオブジェクトは関連する関数を実装するために使用されます。
Proxy.operator()
メソッドの実装は、実際のビジネスロジックを実行するために、それにカプセル化されたRealSubjectオブジェクトのoperator()メソッドを呼び出します。
これが「エージェントモード」です。
上記の例から、プロキシモードを使用すると、プロキシオブジェクトを変更せずにプロキシクラスを拡張することで、一部の機能を追加および拡張できることがわかります。プロキシクラスとプロキシクラスを一緒に実装する必要があることに注意してください。特定のクラスの共通の継承。
動的プロキシ
上記の例では、プロキシモードで「静的プロキシモード」を示しています。これは、プロキシクラスSubjectProxyを事前に定義する必要があるためです。プロキシクラスが多い場合、多くのプロキシクラスが表示されます。
このシナリオでは、動的プロキシを使用する必要があります。動的プロキシを実装する方法はたくさんあります。この章では、JDKのネイティブ動的プロキシについて説明します。
JDK動的プロキシのコード実装:
- 動的プロキシクラス
public class SubjectInvokerHandler implements InvocationHandler {
//真正的业务对象
private Object target;
public SubjectInvokerHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前置业务逻辑");
//真正的业务逻辑
method.invoke(target, args);
System.out.println("执行后置业务逻辑");
return null;
}
public Object getProxy() {
//创建代理对象
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(),
this
);
}
}
動的プロキシのコアはInvocationHandlerインターフェイスであり、渡されたメソッド呼び出しの処理方法を決定するinvoke()メソッドが1つだけあります。
- クライアント:
public class Client {
public static void main(String[] args) {
ISubject realSubject = new RealSubject();
SubjectInvokerHandler invokerHandler = new SubjectInvokerHandler(realSubject);
//获取代理对象
ISubject proxy = (ISubject) invokerHandler.getProxy();
proxy.operator();
}
}
同じプロキシロジックを必要とするビジネスクラスの場合、必要なInvocationHandlerインターフェイス実装クラスは1つだけです。Javaの実行中に、JDKは各RealSubjectクラスに対応するプロキシクラスを動的に生成してJVMにロードし、対応するプロキシインスタンスオブジェクトを作成して上位の呼び出し元に返します。
- 実行効果:
ここでは静的プロキシのような特定のプロキシクラスを実装していませんが、最終的には同じ効果を達成していることがわかります。
JDK動的プロキシ実装の原則
動的プロキシの基本的な使用法を理解した後、動的プロキシの実装原理を見てみましょう。
動的プロキシを作成するためのエントリクラスはProxy.newProxyInstance()
この静的メソッドであり、その3つのパラメータは、動的に生成されたプロキシクラスをロードするためのクラスローダー、ビジネスクラスによって実装されるインターフェイス、およびInvocationHandlerオブジェクトです。(コードは少し長いので、スクリーンショットを撮りましょう):
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
//获取代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
...
//获取代理类的构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{
h});
...
}
...
}
newProxyInstance()
最後に、clのクラスファイルの構築メソッドを介したリフレクションによって生成されたインスタンスが返されます。getProxyClass0()
次のように、取得方法によるcl :
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// 如果指定的类加载器中已经创建了实现指定接口的代理类,则查找缓存;
// 否则通过ProxyClassFactory创建实现指定接口的代理类
return proxyClassCache.get(loader, interfaces);
}
proxyClassCacheは、Proxyクラスで定義された静的フィールドであり、主に、作成されたプロキシクラスをキャッシュするために使用されます。定義は次のとおりです。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
WeakCache.get()メソッドは、最初にキャッシュからプロキシクラスを見つけようとします。見つからない場合は、Factoryオブジェクトを作成し、そのget()メソッドを呼び出してプロキシクラスを取得します。FactoryはWeakCacheの内部クラスです。Factory.get()メソッドはProxyClassFactory.apply()メソッドを呼び出して、プロキシクラスを作成およびロードします。
ProxyClassFactory.apply()メソッドは、最初にプロキシクラスが実装する必要のあるインターフェイスのセットを検出し、次にプロキシクラスの名前を決定し、次にプロキシクラスを作成してファイルに書き込み、最後にプロキシクラスをロードして戻ります。後で使用するための対応するClassオブジェクトプロキシクラスオブジェクトをインスタンス化します。このクラスのコアコードは次のとおりです。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
// 代理类的前缀是 $Proxy
private static final String proxyClassNamePrefix = "$Proxy";
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
long num = nextUniqueNumber.getAndIncrement();
//代理类的名称是通过包名、代理类名称前缀以及编号这三项组成的
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 生成代理类,并写入文件
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
...
}
}
}
概要
JDK動的プロキシの実装原理は、プロキシクラスを動的に作成し、指定されたクラスローダーを介してそれらをロードし、プロキシオブジェクトを作成するときにInvocationHandlerオブジェクトを構築パラメーターとして渡すことです。プロキシオブジェクトが呼び出されると、InvocationHandler.invoke()メソッドが呼び出されてプロキシロジックが実行され、最後に実際のビジネスオブジェクトの対応するメソッドが呼び出されます。
総括する
この章の内容は、主にデザインモードのプロキシモードについて説明しています。プロキシモードの役割は、プロキシオブジェクトのソースコードを変更せずに機能を拡張することです。この開発モデルは、AOPアスペクト指向プログラミングの分野で非常に一般的です。
プロキシモードでは、静的プロキシは独自のプロキシクラスを作成する必要があり、動的プロキシのプロキシクラスはProxy.newInstance()メソッドによって生成されます。その本質はインターフェイス指向プログラミングです。
では、インターフェースの代わりにプロキシを実装したい場合はどうでしょうか。またね。いいえ、最初に本を配達する必要があります。
本の寄付
「Javaコアプログラミング」LiuWeiwei
清華大学出版局の支援に感謝します。
本の寄贈方法
シンプルで失礼なことですが、公式アカウントにメッセージを残してください。いいねの数が最も多い人が1部を受け取ります。
時間は10月15日の20時に終了します。
これは皆へのささやかな贈り物です。公式アカウントに従って、次のコードを入力してください。Baiduネットワークディスクアドレスを取得できます。ルーチンはありません。
001:「プログラマーにとって必読の本」
002:「中小規模のインターネット企業向けのバックエンドサービスアーキテクチャと運用および保守アーキテクチャをゼロから構築する」
003:「インターネット企業向けの高同時実行ソリューション」
004:「インターネットアーキテクチャ教育ビデオ "
006:
注文システムのSpringBoot実現" 007: "SpringSecurity実際の戦闘ビデオ"
008: "Hadoop実際の戦闘教育ビデオ"
009: "Tencent 2019 Techo Developer Conference PPT"
010:WeChat交換グループ