目次:
- 1.代理店モデル
- 2.静的プロキシ
- 3.動的エージェント
- 3.1。JDK動的プロキシメカニズム
- 3.2。CGLIB動的プロキシメカニズム
- 3.3。JDK動的プロキシとCGLIB動的プロキシの比較
- 4.静的プロキシと動的プロキシの比較
- 5.まとめ
1.代理店モデル
エージェントモデルは、よりよく理解された設計パターンです。簡単に言うと、 プロキシオブジェクトを使用して実際のオブジェクトへのアクセスを置き換えるため、追加の機能操作を提供し、元のターゲットオブジェクトを変更せずにターゲットオブジェクトの機能を拡張できます。
プロキシモードの主な機能は、ターゲットオブジェクトの機能を拡張することです。たとえば、ターゲットオブジェクトの特定のメソッドが実行される前後に、カスタム操作を追加できます。
たとえば、あなたがXiaohongにあなたが尋問するのを手伝うように頼んだ、Xiaohongは私のエージェントと見なされ、エージェントの行動(方法)は尋問でした。
プロキシモードには、静的プロキシと動的プロキシの2つの実装方法があります。まず、静的プロキシモードの実装を見てみましょう。
2.静的プロキシ
静的プロキシでは、ターゲットオブジェクトの各メソッドの拡張は手動で行われ(コードについては後で詳しく説明します)、非常に柔軟性がありません(たとえば、新しいメソッドをインターフェイスに追加したら、ターゲットオブジェクトとプロキシオブジェクトを変更する必要があります)。 (ターゲットクラスごとに個別のプロキシクラスを記述する必要があります)。 実際のアプリケーションシナリオはごくわずかであり、静的エージェントの使用は日常の開発ではほとんど見えません。
上記は、実装とアプリケーションの観点から静的エージェントであり、JVMの観点から、 静的エージェントは、インターフェース、実装クラス、およびプロキシクラスをコンパイル時に実際のクラスファイルに変換します。
静的プロキシの実装手順:
- インターフェイスとその実装クラスを定義します。
- このインターフェイスも実装するプロキシクラスを作成する
- ターゲットオブジェクトをプロキシクラスに挿入し、プロキシクラスの対応するメソッドでターゲットクラスの対応するメソッドを呼び出します。このようにして、プロキシクラスを介したターゲットオブジェクトへのアクセスをブロックし、ターゲットメソッドが実行される前後に必要な処理を実行できます。
次のコードが示しています!
1. SMSを送信するためのインターフェースを定義する
2. SMSを送信するためのインターフェースを実装する
3.プロキシクラスを作成し、SMSを送信するためのインターフェイスも実装する
4.実際の使用
上記のコードを実行すると、コンソールは次のように出力します。
出力からわかるように、SmsServiceImplのsend()メソッドを追加しました。
3.動的エージェント
静的エージェントと比較して、動的エージェントはより柔軟です。ターゲットクラスごとに個別のプロキシクラスを作成する必要はありません。また、インターフェイスを実装する必要もありません。実装クラスを直接プロキシできます( CGLIB動的プロキシメカニズム)。
JVMの観点からは、動的エージェントは実行時にクラスバイトコードを動的に生成し、それをJVMにロードします。
動的プロキシと言えば、Spring AOPとRPCフレームワークは2つ言及する必要があります。それらの実装はすべて動的プロキシに依存しています。
動的エージェントは、私たちの日常の開発では比較的小規模ですが、フレームワークではほぼ必要な技術です。動的エージェントを学んだ後、さまざまなフレームワークの原理を理解して学ぶことも非常に役立ちます。
Javaに関する限り、JDK動的プロキシ、CGLIB動的プロキシなど、動的プロキシを実装するには多くの方法があります 。
guide-rpc-framework [1] はJDKダイナミックプロキシを使用しています。まず、JDKダイナミックプロキシの使用について見てみましょう。
さらに、 guide-rpc-framework [2] はCGLIB動的プロキシを使用しませんが、 その使用と JDK動的プロキシとの比較を簡単に紹介します。
3.1。JDK動的プロキシメカニズム
3.1.1。はじめに
Java動的プロキシメカニズムでは、 InvocationHandler インターフェイスと Proxy クラスがコアです。
Proxyクラスで最も頻繁に使用されるメソッドは、newProxyInstance()です。このメソッドは、主にプロキシオブジェクトを生成するために使用されます。
このメソッドには3つのパラメーターがあります。
- loader :プロキシオブジェクトをロードするために使用されるクラスローダー。
- interfaces :プロキシクラスによって実装される一部のインターフェイス。
- h :InvocationHandlerインターフェースを実装するオブジェクト。
動的プロキシを実装するには、InvocationHandlerも実装して処理ロジックをカスタマイズする必要があります。動的プロキシオブジェクトがメソッドを呼び出すと、このメソッドの呼び出しは、呼び出し用のInvocationHandlerインターフェイスクラスを実装するinvokeメソッドに転送されます。
invoke()メソッドには、次の3つのパラメーターがあります。
- proxy :動的に生成されたプロキシクラス
- method :プロキシクラスオブジェクトによって呼び出されるメソッドに対応します
- args :現在のメソッドmethodのパラメーター
言い換えれば、ときあなたが で作成したプロキシオブジェクトのメソッドを呼び出すnewProxyInstanceを()のプロキシ クラス 、それが実際に呼び出しますのinvoke()メソッド クラスのを実装するのInvocationHandlerのインターフェース 。 メソッドの実行前後の処理など、invoke()メソッドの処理ロジックをカスタマイズできます。
3.1.2。JDK動的プロキシクラスの使用手順
- インターフェイスとその実装クラスを定義します。
- InvocationHandlerをカスタマイズして、invokeメソッドをオーバーライドします。invokeメソッドでは、ネイティブメソッド(プロキシクラスのメソッド)を呼び出し、いくつかの処理ロジックをカスタマイズします。
- Proxy.newProxyInstance(ClassLoader loader、Class <?> [] interfaces、InvocationHandler h)メソッドを使用してプロキシオブジェクトを作成します。
3.1.3。コード例
これは少し空洞で理解が難しいかもしれません。最後の例を見てみましょう!
1. SMSを送信するためのインターフェースを定義する
2. SMSを送信するためのインターフェースを実装する
3. JDK動的プロキシクラスを定義する
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author shuang.kou
* @createTime 2020年05月11日 11:23:00
*/
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
} public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return result;
}
}
invoke()メソッド:動的プロキシオブジェクトがネイティブメソッドを呼び出すと、実際にはinvoke()メソッドが呼び出され、次にinvoke()メソッドがプロキシオブジェクトのネイティブメソッドを呼び出す代わりに使用します。
4.プロキシオブジェクトのファクトリクラスを取得する
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
}
getProxy():主に、Proxy.newProxyInstance()メソッドを介して特定のクラスのプロキシオブジェクトを取得します
5.実際の使用
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");
上記のコードを実行すると、コンソールは次のように出力します。
3.2。CGLIB動的プロキシメカニズム
3.2.1。はじめに
JDK動的プロキシの最も致命的な問題の1つは、インターフェースを実装するクラスのみをプロキシできることです。
この問題を解決するために、CGLIB動的プロキシメカニズムを使用してそれを回避できます。
CGLIB [3](コード生成ライブラリ)は、ASM [4]に基づくバイトコード生成ライブラリであり、実行時にバイトコードを変更して動的に生成できます。CGLIBは、継承を通じてプロキシを実装します。SpringのAOPモジュールなど、多くの有名なオープンソースフレームワークがCGLIB [5]を使用しています。ターゲットオブジェクトがインターフェースを実装する場合、デフォルトでJDK動的プロキシが使用されます。それ以外の場合は、CGLIB動的プロキシが使用されます。
MethodInterceptor インターフェースと Enhancer クラスは、CGLIB動的プロキシメカニズムの 中核です。
MethodInterceptorをカスタマイズしてインターセプトメソッドをオーバーライドする必要があります。インターセプトは、プロキシクラスを拡張するメソッドをインターセプトするために使用されます。
public interface MethodInterceptor
extends Callback{
// 拦截被代理类中的方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
- obj :プロキシされるオブジェクト(拡張する必要があるオブジェクト)
- method :インターセプトされたメソッド(拡張が必要なメソッド)
- args :メソッドパラメータ
- methodProxy :元のメソッドを呼び出すために使用されます
Enhancerクラスを介して動的にプロキシクラスを取得できます。プロキシクラスがメソッドを呼び出すと、MethodInterceptorのインターセプトメソッドが実際に呼び出されます。
3.2.2。CGLIB動的プロキシクラスを使用する手順
- クラスを定義します。
- MethodInterceptorをカスタマイズしてインターセプトメソッドをオーバーライドするインターセプトメソッドは、JDKダイナミックプロキシのinvokeメソッドと同様に、プロキシクラスメソッドをインターセプトおよび拡張するために使用されます。
- Enhancerクラスのcreate()を使用してプロキシクラスを作成します。
3.2.3。コード例
JDK動的プロキシとは異なり、追加の依存関係は必要ありません。CGLIB [6](コード生成ライブラリ)は実際にはオープンソースプロジェクトに属しています。使用する場合は、関連する依存関係を手動で追加する必要があります。
1. Alibaba Cloudを使用してSMSを送信するクラスを実装する
2.カスタム MethodInterceptor (メソッドインターセプター)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class DebugMethodInterceptor implements MethodInterceptor {
/**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object object = methodProxy.invokeSuper(o, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return object;
}
}
3.プロキシクラスを取得する
4.実際の使用
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");
上記のコードを実行すると、コンソールは次のように出力します。
3.3。JDK動的プロキシとCGLIB動的プロキシの比較
- JDK動的プロキシーは、インターフェースを実装するクラスのみをプロキシーできますが、CGLIBはインターフェースを実装しないクラスをプロキシーできます。 さらに、CGLIB動的プロキシーは、プロキシー・クラスのサブクラスを生成することにより、プロキシー・クラスのメソッド呼び出しをインターセプトするため、finalとして宣言されたクラスおよびメソッドをプロキシーすることはできません。
- 両者の効率の点では、ほとんどの場合、JDKダイナミックプロキシの方が優れていますが、JDKバージョンのアップグレードにより、この利点がより明確になります。
4.静的プロキシと動的プロキシの比較
- 柔軟性 :動的プロキシはより柔軟であり、インターフェースを実装する必要がありません。実装クラスを直接プロキシすることができ、ターゲットクラスごとにプロキシクラスを作成する必要はありません。さらに、静的プロキシでは、新しいメソッドがインターフェースに追加されると、ターゲットオブジェクトとプロキシオブジェクトを変更する必要があり、これは非常に面倒です。
- JVMレベル :静的プロキシは、インターフェース、実装クラス、およびプロキシクラスをコンパイル時に実際のクラスファイルに変換します。動的エージェントは、実行時にクラスバイトコードを動的に生成し、それをJVMにロードします。
5.まとめ
この記事では主に、静的プロキシと動的プロキシという2つのプロキシモードの実装を紹介します。静的エージェントと動的エージェントの実際の戦闘、静的エージェントと動的エージェントの違い、JDK動的エージェントとCglib動的エージェントの違いについて説明します。
高品質の記事の推奨読書:
アリババの高度な面接質問(最初の問題、136のよくある質問、回答を含む)
https://blog.csdn.net/weixin_45132238/article/details/107251285
GitHub Biaoxing 20wの4つの低レベルインタビューガイド(コンピューターのボトムレイヤー+オペレーティングシステム+アルゴリズム)、インタビューのヘッドライン/ Tencentは正しいです!
https://blog.csdn.net/weixin_45132238/article/details/108640805
Alibaba内部アーキテクチャの戦闘:SpringBoot / SpringCloud / Docker / Nginx / distributed
https://blog.csdn.net/weixin_45132238/article/details/108666255