English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Retrofitは非常に高い解耦性を持つネットワークリクエストフレームワークであり、最近研究を進める中で非常に強力で実用的な技術である動的プロキシを発見しました。この記事はRetrofitの前置知識として役立ち、以下について説明します:動的プロキシの適用シーンは何か、動的プロキシとは何か、どのように使用するか、その限界はどこにあるか?
動的代理の適用シナリオ
1. AOP(面向切面编程)- プログラムの解耦
簡単に言えば、特定のクラスの内部のメソッドに対して、実行前後で共通の操作を行い、メソッド内で個別の操作を行いたい場合--動的代理を使用すると、業務量が多い場合にコード量を減らし、メンテナンス性を高めることができます。
2. サードパーティーライブラリの一部のメソッドをカスタマイズしたい
私はサードパーティーのライブラリを引用していますが、その一部のメソッドは私の要件に合いません。私はそれらのメソッドを自分で再書き込み、またはメソッドの前後で特別な操作を追加したいと考えています。--動的代理を使用しますが、これらの方法には制約があります。その後説明します。
動的代理とは何か
以上の図は非常に抽象的で、私たちは日常生活の例から始めてみましょう。
あなたが大房东(被代理者)で、多くの家を賃貸したいけど、賃客を見つけるのが面倒で自分でやたくないので、代理者を見つけ(代理人)、これらのことを管理させることにしました。この人(代理人または仲介)があなたに家を賃貸する際に適切な仲介料を請求します(家の賃貸に関する追加の操作)。賃客にとって、仲介は所有者であり、あなたに代わっていくつかのことを行います。
以上が、代理の例です。そして、なぜ「動的代理」と呼ばれるのか、「動的」という言葉がどこに現れるのか?
こんな考え方で、あなたのそれぞれの家に代理人を一人ずつ雇えば、再び家を賃貸するときにはまた一人を雇わなければならず、多くの代理人を雇用し、高額な仲介コストがかかることになります。これはよく言われる「静的代理」と考えられます。
しかし、すべての家を仲介に任せ、彼が複数の家の間で動的に役割を切り替え、あなたにそれぞれの賃客に対応させることは、これは「動的代理」のプロセスです。動的代理の大きな特徴は、コンパイル段階で代理クラスが存在せず、実行時に生成されることです。
以下のコードで確認しましょう
住宅の賃貸の操作
/** *インターフェースの定義 **/ public interface RentHouse { void rent();//住宅の賃貸 void charge(String str);//賃貸料金の収益 }
大家主
public class HouseOwner implements RentHouse { public void rent() { System.out.println("I want to rent my house"); } public void charge(String str) { System.out.println("You get : ") + str + "RMB HouseCharge."); } }
仲介
public class DynamicProxy implements InvocationHandler { // これは私たちが代理する実際のオブジェクトです。大家主です。 private Object subject; // 構造方法、私たちが代理する実際のオブジェクトに初期値を設定します。 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 実際のオブジェクトを代理する前に、自分自身の操作を追加することもできます。仲介手数料が徴収されます。 System.out.println("before "+method.getName()+"house"); System.out.println("Method:") + method.getName()); // メソッドがchargeの場合、仲介手数料が徴収されます。100円仲介手数料。 if (method.getName().equals("charge")) { method.invoke(subject, args); System.out.println("I will get 100 RMB ProxyCharge."); } else { // 代理オブジェクトが実際のオブジェクトのメソッドを呼び出したとき、自動的に代理オブジェクトに関連付けられたhandlerオブジェクトのinvokeメソッドに飛び越えます。 method.invoke(subject, args); } // 実際のオブジェクトを代理した後も、自分自身の操作を追加することもできます。 System.out.println("after "+method.getName()+"house"); return null; } }
客
public class Client { public static void main(String[] args) { // 私たちが代理する実際のオブジェクト--大家主 HouseOwner houseOwner = new HouseOwner(); // 私たちが代理する実際のオブジェクトは、そのオブジェクトを入れて、最終的には実際のオブジェクトを通じてそのメソッドを呼び出します。 InvocationHandler handler = new DynamicProxy(houseOwner); /* * ProxyのnewProxyInstanceメソッドを使用して私たちの代理オブジェクトを作成します。その3つの引数を見てみましょう。 * 第1引数handler.getClass().getClassLoader()、ここではhandlerクラスのClassLoaderオブジェクトを使用してプロキシオブジェクトをロードしています * 第2引数realSubject.getClass().getInterfaces()、ここではプロキシオブジェクトに提供するインターフェースは実際のオブジェクトが実行するインターフェースであり、私はこの実際のオブジェクトを代理したいと示しています。このようにして、このインターフェースグループのメソッドを呼び出すことができます * 第3引数handler、ここではこのプロキシオブジェクトを上記のInvocationHandlerオブジェクトに関連付けました */ RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner .getClass().getInterfaces(), handler);//動的プロキシクラス、仲介 System.out.println(rentHouse.getClass().getName()); rentHouse.rent(); rentHouse.charge("10000"); } }
出力を見てみましょう
com.sun.proxy.$Proxy0 before rent house メソッド:rent 私の家を借りたい after rent house before charge house メソッド:charge あなたが得るのは: 10000 RMB HouseCharge. I will get 100 RMB ProxyCharge. after charge house プロセスが終了し、終了コード0で終了しました
before rent houseおよびafter rent houseが表示され、方法の前後に操作を追加できることを示しています。さらに、I will getが表示されました 100 RMB ProxyCharge. 仲介が収取しました100円の仲介手数料、これは操作を増やすだけでなく、该方法を置き換えたり、直接実行しないようにできることを示しています。
コードを見始めたばかりの人は多くの疑問を持つかもしれません。以下の内容を通じて、動的プロキシの使用方法を見てみましょう。
動的代理はどのように使用するか
Javaの動的代理メカニズムには、InvocationHandler(インターフェース)、Proxy(クラス)という2つの重要なクラスとインターフェースがあります。これらは、動的代理を実現するために必要なものです。
動的代理クラスは、InvocationHandlerインターフェース(コードの仲介者)を実装する必要があり、各代理クラスのインスタンスはhandlerに関連付けられています。代理オブジェクトを通じてメソッドを呼び出すと、そのメソッドの呼び出しがInvocationHandlerインターフェースのinvokeメソッド(メソッドの強化はここに書かれます)に転送されます。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
このメソッドは3つの引数を受け取っていますが、それぞれの引数が何を表しているのでしょうか?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable //proxy: 代理する実際のオブジェクトを指します //method: 呼び出す実際のオブジェクトのメソッドを表すMethodオブジェクトを指します //args: 呼び出す実際のオブジェクトのメソッドを受け取る引数を指します
次に、Proxyクラスを見てみましょう
public static Object newProxyInstance(ClassLoader loader, Class<63;][インターフェース、 InvocationHandler h) throws IllegalArgumentException
Proxyクラスの役割は、動的代理オブジェクトを動的に作成するためのクラスで、多くのメソッドを提供していますが、最もよく使うのはnewProxyInstanceメソッドです:
public static Object newProxyInstance(ClassLoader loader, Class<63;][インターフェース、 InvocationHandler h) throws IllegalArgumentException
このメソッドの役割は、動的代理オブジェクトを取得することで、それが3つの引数を受け取ります。これらの引数がどのような意味を持つかを見てみましょう
public static Object newProxyInstance(ClassLoader loader, Class<63;][インターフェース、InvocationHandler h) throws IllegalArgumentException //ローダー:ClassLoaderオブジェクトで、生成された代理オブジェクトをどのClassLoaderオブジェクトでロードするかを定義します //インターフェース:一つのInterfaceオブジェクトの配列で、私が代理する必要があるオブジェクトに提供する一連のインターフェースを表します。インターフェースを提供した場合、その代理オブジェクトはそのインターフェースを実装していると宣言します(多態性)、そのため、そのインターフェース内のメソッドを呼び出すことができます //h:動的プロキシオブジェクトが呼び出される際に、どのInvocationHandlerオブジェクトに関連付けられるかを示すInvocationHandlerオブジェクト
これで、上記のコードを結びつけて、動的プロキシの使用方法を理解することができます。
動的プロキシの限界
動的プロキシの使用方法から、実際に強化できるメソッドはインターフェースを実装したもの(インターフェースを実装していないpublicメソッドもプロキシクラスを継承することで使用できます)であることがわかります。コードではHouseOwnerがRentHouseを継承していますが、privateメソッドについてはJDKの動的プロキシは無力です。
上記の動的プロキシはJDKのものですが、Javaプロジェクトには有名なCGLibがありますが、残念ながらCGLibはAndroidで使用できません。AndroidのバーチャルマシンはJVMと異なります。
エンディング
動的プロキシの使用シーンはこれらだけではなく、内部原理は今後の記事で紹介しますが、アプリケーションクラスの反射による一時的なプロキシクラスの生成メカニズムが、性能に多少の影響を与えることを決定します。本記事はretrofitの原理の前置記事であり、詳細ではありません。誤解や間違いがあれば、指正を歓迎します。
これで本文の全てが終わり、皆さんの学習に役立つことを願っています。また、呐喊教程を多くのサポートをお願いします。
声明:本文の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーにより自発的に提供された内容であり、本サイトは所有権を有しておらず、編集も行われていません。著作権侵害が疑われる内容があれば、メールを送信してください:notice#oldtoolbag.com(メールを送信する際には、#を@に変更してください。報告を行い、関連する証拠を提供してください。一旦確認が取れましたら、本サイトは即座に侵害される可能性のある内容を削除します。)