Java-詳細な動的エージェントメカニズム

Springを学ぶとき、Springには2つの主要なアイデアがあることを知っています。1つはIoCで、もう1つはAOPです。IoCの場合、依存性注入は多くのことを言う必要はありません。SpringのコアAOPの場合、渡す方法を知るだけでなく機能を満たすためにAOPを使用するには、基礎となる原理についてさらに学ぶ必要があります。AOPの原理はJavaの動的プロキシメカニズムであるため、このエッセイはJavaの動的メカニズムのレビューです。

Javaの動的プロキシメカニズムには、2つの重要なクラスまたはインターフェイスがあります。1つはInvocationHandler(インターフェイス)、もう1つはプロキシ(クラス)です。このクラスとインターフェイスは、動的プロキシを実現するために必要です。最初に、Java APIヘルプドキュメントでこれらの2つのクラスがどのように記述されているかを見てみましょう。

InvocationHandler:

InvocationHandlerは、プロキシインスタンスの呼び出しハンドラによって実装されるインターフェースです。

各プロキシインスタンスには、関連付けられた呼び出しハンドラがあります。メソッドがプロキシインスタンスで呼び出されると、メソッド呼び出しはエンコードされ、その呼び出しハンドラのinvokeメソッドにディスパッチされます。

各動的プロキシクラスはInvocationHandlerインターフェイスを実装する必要があり、プロキシクラスの各インスタンスはハンドラに関連付けられています。プロキシオブジェクトを介してメソッドを呼び出すと、メソッド呼び出しはInvocationHandlerのインターフェイスに転送されますinvokeメソッドを呼び出す。InvocationHandlerのインターフェースの唯一のinvokeメソッドを見てみましょう

Object invoke(Object proxy、Method method、Object [] args)throws Throwable

このメソッドは合計3つのパラメーターを受け入れることがわかります。したがって、これらの3つのパラメーターは何を表していますか?

オブジェクト呼び出し(オブジェクトプロキシ、メソッド方法,,オブジェクト[]引数)がスローのThrowable プロキシ:  我々は薬剤である実際のオブジェクトを参照して、方法:  我々は実際のオブジェクトのメソッド呼び出したいメソッドのオブジェクトを指す引数を:  を指し実際のオブジェクトでメソッドを呼び出すときにパラメーターは受け入れられますか



理解できない場合は、例を使用してこれらのパラメータについて詳しく説明します。

次に、Proxyクラスを見てみましょう。

プロキシは、動的プロキシクラスとインスタンス作成するための静的メソッド提供し、これらのメソッドによって作成されたすべての動的プロキシクラスのスーパークラスでもあります。

Proxyクラスの役割は、多くのメソッドを提供するプロキシオブジェクトクラスを動的に作成することですが、最もよく使用するのはnewProxyInstanceメソッドです。

public  static Object newProxyInstance(ClassLoader loader、Class <?> [] interfaces、InvocationHandler h)    IllegalArgumentExceptionをスローする
戻るプロキシのインスタンスのクラス のために指定された呼び出しハンドラに対してメソッド呼び出しをディスパッチする、指定されたインタフェース。

このメソッドの役割は、3つのパラメーターを受け取る動的プロキシオブジェクトを取得することです。これら3つのパラメーターの意味を見てみましょう。

コードをコピー
パブリック 静的オブジェクトのnewProxyInstance(クラスローダローダ、クラス[]のInvocationHandler Hのインターフェース、<?>)スローはIllegalArgumentException ローダ:  ClassLoaderオブジェクトは、クラスローダは、オブジェクトそれによって生成されたプロキシオブジェクトをロードするために定義されたインタフェースを:  インタフェースが表すオブジェクトの配列を私が欲しいのは、プロキシする必要のあるオブジェクトに一連のインターフェースを提供することです。それに一連のインターフェースを提供すると、プロキシオブジェクトはインターフェースを実装すると主張し(多態性)、このインターフェースのセットを呼び出すことができます。メソッドはh:  InvocationHandlerオブジェクトで、動的プロキシオブジェクトがメソッドを呼び出すときにどのInvocationHandlerオブジェクトが関連付けられるかを示します。





コードをコピー

さて、これらの2つのインターフェース(クラス)を紹介した後、例を見て、動的プロキシモデルがどのように見えるかを見てみましょう。

まず、Subject型のインターフェースを定義し、そのための2つのメソッドを宣言します。

public  interface Subject 
{ 
    public  void rent(); 
    
    public  void hello(String str); 
}

次に、このインターフェイスを実装するクラスを定義します。このクラスは、実際のオブジェクトであるRealSubjectクラスです。

コードをコピー
パブリック クラス RealSubject サブジェクトを
    実装します
{ 
    @Override public  void rent()
    { 
        System.out.println( "自分の家を借りたい" ); 
    } 
    
    @Override 
    public  void hello(String str)
    { 
        System.out.println( "hello:" + str); 
    } 
}
コードをコピー

次に、動的プロキシクラスを定義する必要があります前述のように、各動的プロキシクラスはInvocationHandlerインターフェイスを実装する必要があるため、動的プロキシクラスも例外ではありません。

コードをコピー
public  class DynamicProxy implements InvocationHandler 
{ 
    //  これは、プロキシする実際のオブジェクトです
    private Object subject; 
    
    //     構築メソッド、プロキシする実際のオブジェクトに初期値を割り当てます
    public DynamicProxy(Object subject)
    { 
        this .subject = subject; 
    } 
    
    @ 
    public Object invoke(Object object、Method method、Object [] args)オーバーライドします
             throws Throwable 
    { 
        //   実際のオブジェクトをプロキシする前に独自の操作の一部を追加できます 
        System.out.println( "before rent house" ); 
        
        System.out。 println( "メソッド:" + メソッド); 
        
        //    プロキシオブジェクトのメソッドは、実際のオブジェクトを呼び出すようにすると、それが自動的に呼び出すためのプロキシオブジェクトに関連付けられているオブジェクトのメソッド・ハンドラを呼び出すためにジャンプします
        Method.invoke(件名、引数を); 
        
        //   エージェント実際のオブジェクトの後に、我々はまた、いくつかを追加することができます独自の操作 
        System.out.println( "after rent house" ); 
        
        戻り値 null ; 
    } 

}
コードをコピー

最後に、Clientクラスを見てみましょう。

コードをコピー
public  class Client 
{ 
    public  static  void main(String [] args)
    { 
        //     プロキシしたい実際のオブジェクト 
        Subject realSubject = new RealSubject(); 

        //     プロキシしたい実際のオブジェクト、オブジェクトを渡し、最後に渡しますメソッドを呼び出す実際のオブジェクト 
        InvocationHandler handler = new DynamicProxy(realSubject); 

        / * 
         *プロキシのnewProxyInstanceメソッドでプロキシオブジェクトを作成します。3つのパラメータを見てみましょう
         *最初のパラメータhandler.getClass() .getClassLoader()、ハンドラクラスのClassLoaderオブジェクトを使用してプロキシオブジェクトをロードします
         * 2番目のパラメータrealSubject.getClass()。getInterfaces()、プロキシオブジェクト用にここで提供するインターフェイスは、実際のオブジェクトによって実装されるインターフェイスです、実際のオブジェクトをプロキシし、このインターフェイスのセットのメソッドを呼び出すことができることを示します
         * 3番目のパラメーターハンドラー。このプロキシオブジェクトを上記のInvocationHandlerオブジェクトに関連付けます
         * / 
        サブジェクトサブジェクト =(Subject)Proxy.newProxyInstance(handler.getClass()。getClassLoader()、realSubject 
                .getClass()。getInterfaces()、handler); 
        
        System.out.println(subject.getClass()。getName()); 
        subject.rent(); 
        subject.hello( "world" ); 
    } 
}
コードをコピー

最初にコンソールの出力を見てみましょう:

コードをコピー

家を借りる前の$ Proxy0
 
メソッド:public  abstract  void com.xiaoluo.dynamicproxy.Subject.rent()
を借りる

前にを借りた後、家を借りたい
メソッド:public  abstract  void com.xiaoluo.dynamicproxy.Subject.hello(java。 lang.String) 
hello:
家を借りた後の世界
コードをコピー

最初に$ Proxy0を見てみましょう。これはSystem.out.println(subject.getClass()。GetName())によって出力されることがわかります。これが、このプロキシオブジェクトを返した理由です。クラス名はこんな感じ?

件名件名= (Subject)Proxy.newProxyInstance(handler.getClass()。getClassLoader()、realSubject 
                .getClass()。getInterfaces()、handler);

返されたプロキシオブジェクトは、Subject型のオブジェクト、またはInvocationHandlerのオブジェクトであると思ったかもしれませんが、結果はそうではありません。最初に、なぜここでSubject型のオブジェクトに変換できるのかを説明しましょうその理由は、newProxyInstanceメソッドの2番目のパラメーターで、プロキシオブジェクトのインターフェイスのセットを提供し、プロキシオブジェクトがこのインターフェイスのセットを実装するためです。現時点では、プロキシオブジェクトを必須の型変換に変換できます。このグループのインターフェースのいずれか1つでは、ここのインターフェースはサブジェクトタイプであるため、サブジェクトタイプに変換できます

同時に、Proxy.newProxyInstanceによって作成されたプロキシオブジェクトは、jvmの実行中に動的に生成されるオブジェクトであることを覚えておく必要があります。これは、InvocationHandlerタイプでも定義したインターフェースグループのタイプでもありませんが、実行中です。動的に生成されたオブジェクト。命名方法はこの形式で、$で始まり、プロキシがあり、最後の数字はオブジェクトのラベルを示します

次に、これらの2つの文を見てみましょう 

subject.rent();
subject.hello( "world");

プロキシオブジェクトを介して実装された種類のインターフェイスのメソッドは次のとおりです。このとき、プログラムはプロキシオブジェクトに関連付けられたハンドラーのinvokeメソッドにジャンプして実行し、ハンドラーオブジェクトはRealSubjectを受け入れます。 typeパラメーターは、プロキシするのがこの実際のオブジェクトであることを示しているため、この時点で、ハンドラーのinvokeメソッドが呼び出されて実行されます。

コードをコピー
public Object invoke(Object object、Method method、Object [] args)
             throws Throwable 
    { 
        //   実際のオブジェクトをプロキシする前に独自の操作の一部を追加できます 
        System.out.println( "before rent house" ); 
        
        System.out.println (「方法,:」+ 方法); 
        
        //     プロキシオブジェクトのメソッドを呼び出したときに自動的にコールにプロキシオブジェクト・ハンドラに関連付けられたメソッドを呼び出すためにジャンプする実際のオブジェクトは、オブジェクト
        Method.invoke(件名、引数); 
        
        / /   実際のオブジェクトをプロキシした後、独自のオペレーション 
        System.out.println( "after rent house" );を
        
        返す こともできます; nullを返す; 
    }
コードをコピー

実際のオブジェクトのメソッドがプロキシオブジェクトを介して呼び出されると、メソッドの前後に独自の操作の一部を追加でき、メソッドオブジェクトは次のようになります。

public  abstract  void com.xiaoluo.dynamicproxy.Subject.rent()

public  abstract  void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

これは、Subjectインターフェースの2つのメソッドですが、これは、プロキシオブジェクトを介してメソッドを呼び出すと、自分ではなく、関連付けられているハンドラーオブジェクトのinvokeメソッドによって呼び出されるように委譲されていることを示しています。実際の通話ですが、エージェントを介して通話します。

これは私たちのJava動的プロキシメカニズムです

 

このエッセイでは、Javaの動的プロキシメカニズムについて詳しく説明していますが、動的プロキシメカニズムを通じて実装されるSpring AOPを含め、この知識ポイントは非常に重要なので、動的プロキシメカニズムをよく理解する必要があります。


リリース7件のオリジナルの記事 ウォン称賛69 ビュー200,000 +

おすすめ

転載: blog.csdn.net/u014320421/article/details/79770743