jdkダイナミックエージェントとCGLIBダイナミックエージェントの実現と区別

30904 ワード

前回は静的エージェント設計モードのエージェントモード【1】静的エージェント今日javaの動的エージェントについて述べた.
一、なぜ動的エージェントを使用するのか
実際、動的エージェントは静的エージェントを補うショートボードであり、静的エージェントは各被エージェントオブジェクトにエージェントクラスを書く必要がある.これは大量の冗長コードを書く必要があるが、動的エージェントは、1つのエージェントクラスを書くだけで、複数の異なるオブジェクトをエージェントすることができ、同じインタフェースを実現することを前提として、重複コードを大幅に減らすことができる.エージェントの主な目的は、ターゲットオブジェクトへのアクセスを制御することであり、ターゲットオブジェクトの機能を強化することではありません.アクセス制御には、同期、認証、リモートアクセス(RPC)、怠惰インスタンス化(Hibernate、Mybatis)、AOP(トランザクション)が含まれます.
二、動的代理実現方式
2.1 jdkダイナミックエージェント
まず、お見合いデートに行くシーンを設定します.デート相手は小花ですが、私は直接小花に連絡することができません.私は仲人の紹介を通じて小花とデートする必要があります.この时、小花は代理対象で、仲人は代理対象で、私はプログラムの発起人です.
デートインタフェース:2つの機能、映画を見ることと昼食を食べること
package com.example.demo1.Dynamic;

//        
public interface YueHui {
    //   
    void movie();
    //   
    void lunch();
}

小花:デートインタフェースの実装
/**
 *     
 *        
 * create by c-pown on 2020-06-29
 */
public class XiaoHua implements YueHui {

    @Override
    public void movie() {
        System.out.println("      ");
    }

    @Override
    public void lunch() {
        System.out.println("       ");
    }
}

エージェントオブジェクト(メディア)コア:InvocationHandlerインタフェースを実装し、invokメソッドを再書き込みし、反射によってオブジェクトメソッドを呼び出し、コンテキストを再拡張することができます.getObj()メソッドは、インタフェース実装オブジェクトを返すことができる.
/**
 *         
 *     InvocationHandler  
 * create by c-pown on 2020-06-29
 */
public class ProxyPerson implements InvocationHandler {

    //        (  )
    private Object object;

    //     
    public ProxyPerson(Object object) {
        this.object = object;
    }
    //     :
    // proxy:      (            method()
    // method:          
    // args:          
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //                  :    ,     
        System.out.println("       ");
        method.invoke(object,args);
        System.out.println("       ");
        return null;
    }

    //      
     //   this::invok       this,      invok  
    public Object getObj(){
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this::invok);
    }
}

テストプライマリクラス:被エージェントをエージェントオブジェクトに初期化し、インタフェースオブジェクトに直接戻り、メソッドを呼び出す必要があります.
/**
 *       
 * create by c-pown on 2020-06-29
 */
public class TestDynamicProx {
    public static void main(String[] args) {
        //        
        XiaoHua xiaoHua = new XiaoHua();
        //        ,       
        ProxyPerson proxyPerson = new ProxyPerson(xiaoHua);
        //      
        YueHui yueHui = (YueHui)proxyPerson.getObj();
        yueHui.lunch();
        yueHui.movie();
    }
}

実行結果:
       
       
       
       
      
       

エージェントが実装され、強化が完了しました.
もし私と小花双方が満足していなければ、仲人さんはまた私に新しい女の子を紹介してくれました.静さん.この时、私たちは再び静のために代理クラスを書く必要はありません.静がデートインタフェースを実現するだけでいいです.
/**
 *       
 *        
 * create by c-pown on 2020-06-29
 */
public class XiaoJing implements YueHui {

    @Override
    public void movie() {
        System.out.println("      ");
    }

    @Override
    public void lunch() {
        System.out.println("       ");
    }
}

同じエージェントオブジェクトは、異なるオブジェクトを複数回エージェントできます.
package com.example.demo1.Dynamic;
/**
 *       
 * create by c-pown on 2020-06-29
 */
public class TestDynamicProx {
    public static void main(String[] args) {
        //        
        XiaoHua xiaoHua = new XiaoHua();
        //        ,       
        ProxyPerson proxyPerson = new ProxyPerson(xiaoHua);
        //      
        YueHui yueHui = (YueHui)proxyPerson.getObj();
        yueHui.lunch();
        yueHui.movie();
        System.out.println("-------------   -------------");
        //        
        XiaoJing xiaoJing = new XiaoJing();
        //        ,       
        ProxyPerson proxyPerson1 = new ProxyPerson(xiaoJing);
        //      
        YueHui yueHui1 = (YueHui)proxyPerson1.getObj();
        yueHui1.lunch();
        yueHui1.movie();
    }
}

結果:
       
       
       
       
      
       
-------------   -------------
       
       
       
       
      
       

はい、後ろに何人の女の子がいても、美ちゃん、芳ちゃん、仲人が一人で十分です.
2.2 CGLIBエージェント
JDKで動的エージェントを行うクラスは,1つのインタフェースを実現しなければならない,すなわち,そのクラスが実現するインタフェースで定義された方法のみをエージェントすることができ,これは実際のプログラミングにおいて一定の限界があり,反射を用いる効率もそれほど高くない.
CGLibを使用して動的エージェントを実装することは、エージェントクラスがインタフェースを実装しなければならないという制限を全く受けず、CGLibの下位層はASMバイトコード生成フレームワークを採用し、バイトコード技術を使用してエージェントクラスを生成し、Java反射を使用するよりも効率的である.唯一注意すべきことは、CGLibは、CGLib原理が被エージェントクラスのサブクラスを動的に生成するため、finalとして宣言されたメソッドをエージェントすることができないことである.
対象小美:ここにはインタフェースがありますか.
/**
 *     
 * create by c-pown on 2020-06-29
 */
public class XiaoMei {
    public String name;

    public XiaoMei() {

    }

    public XiaoMei(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void movie(){
        System.out.println("      ");
    }
}

エージェントクラス:MethodInterceptorインタフェースを実装し、Enhancerによって新しいクラスを作成し、ターゲットクラスを継承し、interceptメソッドによって親クラスのメソッドを書き換え、動的エージェントの効果も実現します.
package com.example.demo1.Dynamic;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib      
 * create by c-pown on 2020-06-29
 */
public class CglibInterceptor implements MethodInterceptor {

    //     
    private Object object;

    public CglibInterceptor(Object object) {
        this.object = object;
    }

    public Object getObj(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.object.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("       ");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("       ");
        return o1;
    }
}
XiaoMei xiaoMei = new XiaoMei("  ");
System.out.println(xiaoMei.getName());
 CglibInterceptor cglibInterceptor = new CglibInterceptor(xiaoMei);
 XiaoMei obj = (XiaoMei)cglibInterceptor.getObj();
 obj.movie();

結果:
       
      
       

エージェントを実装することもできます.
注意:CGLIBエージェントはメソッドレベルでエージェントを行い、エージェントオブジェクトの属性を取得できません.例:
/**
 *     
 * create by c-pown on 2020-06-29
 */
public class XiaoMei {
    public String name;

    public XiaoMei() {

    }

    public XiaoMei(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void movie(){
        System.out.println(" "+name+"   ");
    }
}

私たちは美ちゃんの中のmovieメソッドにname属性を追加して取得し、美ちゃんを初期化します.1回呼び出す:
  
       
 null   
       

名前の値がnullであることが判明したのは,CGLIB動的エージェントであり,ターゲットオブジェクトの属性をエージェントできないことを示している.