Java設計モードのダイナミックエージェント


動的エージェントの意味は、実際のオブジェクトをエージェントし、実際のオブジェクトへのアクセスを制御するために、占有(エージェントオブジェクトとも呼ばれる)を生成することです.
まず、エージェントモデルとは何かについてお話しします.もしお客様が需要を持って会社を探しに行くとしたら、明らかにソフトウェアエンジニアと直接話すのではなく、ビジネスと話しています.この時、お客様はビジネスが会社を代表していると思っています.お客様はビジネスを通じてソフトウェアエンジニアにアクセスしています.ビジネス(エージェントオブジェクト)はソフトウェアエンジニア(リアルオブジェクト)をエージェントしていると考えられます.したがって、エージェントの役割は、リアルオブジェクトがアクセスする前または後に対応する論理を加えたり、他のルールに基づいてリアルオブジェクトを使用するかどうかを制御したりすることです.
ビジネスとソフトウェアエンジニアはエージェントと被エージェントの関係であり、お客様はビジネスを通じてソフトウェアエンジニアにアクセスします.このとき、お客様はプログラムの呼び出し者であり、ビジネスはエージェントオブジェクトであり、ソフトウェアエンジニアは真実のオブジェクトである.呼び出し元がオブジェクトを呼び出す前にエージェントオブジェクトを生成する必要があります.このエージェントオブジェクトは実際のオブジェクトとエージェント関係を確立する必要があります.したがって、エージェントは2つのステップに分けなければなりません.
  • エージェントオブジェクトと実オブジェクトとのエージェント関係を確立する
  • エージェントオブジェクトを実装するエージェント論理方法
  • JavaにはJDK、CGLIB、Javassist、ASMなど多くの動的エージェント技術があり、その中で最もよく使われる動的エージェント技術は2種類ある.1つはJDK動的エージェントであり、これはJDKが持参した機能である.もう1つはCGLIBであり、これはサードパーティが提供する技術である.現在、SpringではJDKとCGLIBがよく使われているが、MyBatisではJavassistも使用されている.(CGLIBの下地原理はASM)
    本論文では,JDKとCGLIBの2つの最も一般的な動的エージェント技術のみを論じた.JDkダイナミックエージェントではインタフェースを使用する必要がありますが、CGLIBは必要ありませんので、CGLIBを使用すると簡単です.以下,この2つの技術を順次検討する.
    一.JDKダイナミックエージェント
    JDKダイナミックエージェントはjava.lang.reflect.*パッケージの提供方法.エージェントオブジェクトを生成するには、インタフェースを使用する必要があります.まず、インタフェースHelloWorldを定義します.
    public interface HelloWorld {
        void sayHelloWorld();
    }

    次に、インタフェースを実装する実装クラスHelloWorldImplを提供します.
    public class HelloWorldImpl implements HelloWorld {
        @Override
        public void sayHelloWorld(){
            System.out.println("Hello World");
        }
    }

    これは最も簡単なJavaインタフェースと実装クラスの関係であり,動的エージェントを開始することができる.私たちの前の分析によると、まずエージェントオブジェクトと実際のオブジェクトの関係を確立し、その後、エージェントロジックを実現するために、2つのステップに分かれています.JDK動的エージェントでは,エージェント論理クラスがjavaを実現する必要がある.lang.reflect.InvocationHandlerインタフェースは、invokeメソッドを定義し、プロキシオブジェクトをドロップダウンするためのインタフェース配列を提供します.JDKダイナミックエージェントのクラスを定義します.
    public class MyJdkProxyExample implements InvocationHandler{
    
        /**
         * @description     
         */
        private Object target = null;
    
        /**
         * @author haozz
         * @date 2018-05-21 10:54
         * @param target     
         * @return     
         * @throws
         * @description                 ,       
         */
        public Object getProxy(Object target) {
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        }
    
        /**
         * @author haozz
         * @date 2018-05-21 11:09
         * @param proxy     
         * @param method       
         * @param args       
         * @return     
         * @throws Throwable   
         * @description       
         */
        @Override
        public Object invoke(Object proxy, Method method,Object [] args) throws Throwable{
            System.out.println("        ");
            System.out.println("            ");
            Object obj = method.invoke(target,args);//     sayHelloWorld  
            System.out.println("            ");
            return obj;
        }
    }

    最初のステップでは、getProxy()メソッドでエージェントオブジェクトと実際のオブジェクトの関係を確立し、エージェントオブジェクトに戻ります.まずクラスのプロパティtargetでリアルオブジェクトを保存し、Proxy.新ProxyInstance()メソッドは、3つのパラメータを含むプロキシオブジェクトを作成し、生成します.
  • の1つ目はクラスローダであり、ここではtarget自体のクラスローダを採用している.
  • の2番目は,生成された動的エージェントオブジェクトをどのインタフェースの下に掛けるかであり,target実装インタフェースの下に置く.HelloWorldImplオブジェクトのインタフェースは明らかにHelloWorldであり、エージェントオブジェクトはこのように宣言することができる:HelloWorld proxy=xxxx;
  • 3番目は実装メソッドロジックを定義するエージェントクラスであり、thisは現在のオブジェクトを表し、InvocationHandlerインタフェースのinvokeメソッドを実装しなければならない.これがエージェントロジックメソッドの現実的なメソッドである.

  • 第2ステップでは,invoke法によりエージェント論理法を実現する.invokeメソッドの3つのパラメータ:
  • proxy、エージェントオブジェクト、getProxyメソッドが生成したオブジェクトです.
  • method、現在のスケジューリング方法;
  • args、現在のスケジューリング方法のパラメータ.

  • エージェントオブジェクトスケジューリングメソッドを使用すると、invokeメソッドに入ります.
    Object obj = method.invoke(target,args);

    この行のコードは、反射によって実現されるだけで、実際のオブジェクトをスケジューリングする方法に相当します.クラス比の前の例では、proxyはビジネスに相当し、targetはソフトウェアエンジニアに相当し、getProxyメソッドはビジネスとソフトウェアエンジニアの間のエージェント関係を確立し、invokeメソッドはビジネスロジックである.
    JDKダイナミックエージェントのテスト:
        @Test
        public void testJdkProxy(){
            MyJdkProxyExample jdkProxy = new MyJdkProxyExample();
            //    ,      HelloWorld ,        HelloWorld proxy
            HelloWorld proxy = (HelloWorld) jdkProxy.getProxy(new HelloWorldImpl());
            //  HelloWorld           ,           invoke 
            proxy.sayHelloWorld();
        }

    まずgetProxyメソッドでエージェント関係をバインドし、次にエージェントオブジェクトがsayHelloWorldメソッドをスケジューリングするときにエージェントの論理に入ります.テスト結果は次のとおりです.
            
                
    Hello World
                

    この場合、Hello Worldの印刷をスケジュールする前と後に関連する論理を加えることができ、Hello Worldの印刷をスケジュールしなくてもよい.
    個人小結:JDKダイナミックエージェントは、実オブジェクトとエージェントオブジェクトの間に実装クラスとインタフェースの関係があることを要求し、エージェント論理クラス実装InvocationHandlerインタフェースを作成し、getProxyメソッドはエージェントオブジェクトを生成し、invokeメソッドを書き換える.
    二.CGLIBダイナミックエージェント
    JDKダイナミックエージェントはインタフェースを提供しなければ使用できません.インタフェースを提供できない環境では、CGLIBダイナミックエージェントなどの他のサードパーティ技術しか採用できません.ここではCGLIBダイナミックエージェントに関するjarパッケージを提供し、学習とテストのために使用します.インタフェースを提供する必要がなく、抽象的でないクラスだけで動的エージェントを実現できるという利点があります.
    CGLIB原理:エージェントクラスのサブクラスを動的に生成し、サブクラスがエージェントクラスのすべてを書き換えるのはfinalではない方法である.サブクラスでメソッドブロックの技術を用いてすべての親メソッドの呼び出しをブロックし,横カットロジックを勢いよく織り込む.Java反射を使用するJDKダイナミックエージェントよりも高速です.CGLIB下位層:バイトコード処理フレームワークASMを使用して、バイトコードを変換し、新しいクラスを生成する.JVMの内部構造にclassファイルを含むフォーマットとコマンドセットを熟知する必要があるため、ASMの直接使用は奨励されません.CGLIBの欠点:finalメソッドではエージェントができません.CGLIBはSpring AOPやdynaopのような多くのAOPのフレームワークで広く使用されている.HibernateはCGLIBを使用して、シングルエンドsingle-ended(複数対1および1対1)関連付けを代理します.
    次のような例を挙げます.
    public class ReflectServiceImpl {
        public void sayHello(String name){
            System.out.println("Hello "+name);
        }
    }

    実装インタフェースは存在しないため、JDKダイナミックエージェントは使用できません.ここではCGLIBダイナミックエージェント技術を採用しています.
    public class MyCglibProxyExample implements MethodInterceptor{
    
        /**
         * @author haozz
         * @date 2018-05-21 15:11
         * @param cls Class 
         * @return Class  CGLIB  
         * @throws
         * @description   CGLIB    
         */
        public Object getProxy(Class cls){
            //CGLIB     
            Enhancer enhancer = new Enhancer();
            //      
            enhancer.setSuperclass(cls);
            //             ,        MethodInterceptor  
            enhancer.setCallback(this);
            //         
            return enhancer.create();
        }
    
        /**
         * @author haozz
         * @date 2018-05-21 15:17
         * @param proxy     
         * @param method     
         * @param args       
         * @param methodProxy     
         * @return       
         * @throws Throwable   
         * @description       
         */
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable{
            System.out.println("       ");
            //CGLIB          
            Object result = methodProxy.invokeSuper(proxy,args);
            System.out.println("       ");
            return result;
        }
    }

    ここではCGLIBの補強者Enhancer(net.sf.sglib.proxy.Enhancer)を用いて、スーパークラスのメソッド(setSuperclass)を設定し、setCallbackメソッドでどのクラスがそのエージェントクラスであるかを設定します.ここで、パラメータthisは現在のオブジェクトを表し、これはthisという対象でインタフェースMethodInterceptorを実現する必要があります(net.sf.sglib.proxy.MethodInterceptor)のメソッドinterceptは、プロキシオブジェクトに戻ります.この場合、現在のクラスのinterceptメソッドは、そのプロキシロジックメソッドであり、そのパラメータの内容はコード注記を参照してください.リアルオブジェクトメソッドを反射する前後に印刷されます.CGLIBは、次のコードで完了します.
    Object result = methodProxy.invokeSuper(proxy,args);

    CGLIBダイナミックエージェントをテストします.
        @Test
        public void testCglibProxy(){
            MyCglibProxyExample cglibProxy = new MyCglibProxyExample();
            ReflectServiceImpl obj = (ReflectServiceImpl) cglibProxy.getProxy(ReflectServiceImpl.class);
            obj.sayHello("haozz");
        }

    結果:
           
    Hello haozz
           
    個人小結:CGLIBダイナミックエージェントは比較的簡単で、バイトコード技術によってエージェントを必要とするクラスにサブクラスを生成し、インタフェースを提供する必要はなく、エージェント論理クラスを1つ書いてMethodInterceptorインタフェースを実現するだけで、getProxyメソッドはエージェントオブジェクトを生成し、interceptメソッドを書き換えることができる.