動態代理模式

代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能.
這裏使用到編程中的一個思想:不要隨意去修改別人已經寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴展該方法,

JDK動態代理模式

需要用到的類:

  • 一個實現invocationHandle接口的代理類
  • 需要代理的接口
  • 接口的實現類

代理的接口:

//媒婆
public class Meipo implements InvocationHandler {
    
    private Person target; //被代理對象的引用作爲一個成員變量保存下來了
    
    //獲取被代理人的個人資料
    public Object getInstance(Person target) throws Exception{
        this.target = target;
        Class clazz = target.getClass();
        System.out.println("被代理對象的class是:"+clazz);
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        System.out.println("我是媒婆:" + "得給你找個異性才行");
        System.out.println("開始進行海選...");
        System.out.println("------------");
        
        //調用的時候
        method.invoke(this.target, args);
        System.out.println("------------");
        System.out.println("如果合適的話,就準備辦事");
        
        return null;
    }

}

接口類:

public interface Person { 
    //尋找真愛、相親
    void findLove();  
}

具體實現類 -> 被代理類

//小星星、單身
public class XiaoXingxing implements Person {

    @Override
    public void findLove() {
        System.out.println("高富帥");
        System.out.println("有房有車的");
        System.out.println("身高要求180cm以上,體重70kg");
    }
 
    //回顧一下,滿足代理模式應用場景的三個必要條件,窮取法
    //1、兩個角色:執行者、被代理對象
    //2、注重過程,必須要做,被代理對象沒時間做或者不想做(怕羞羞),不專業
    //3、執行者必須拿到被代理對象的個人資料(執行者持有被代理對象的引用)
}

具體測試:

        // 這裏獲取拿到具體的實現類,通過Meipo這個類進行代理拿到具體的接口對象進行嗲用
    Person obj = (Person)new Meipo().getInstance(new XiaoXingxing());
    System.out.println(obj.getClass());
        // 當
    obj.findLove();

總結

代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理

Cglib代理

  1. 實現MethodInterceptor接口

ublic class GPMeipo implements MethodInterceptor{

    //疑問?
    //好像並沒有持有被代理對象的引用
    public Object getInstance(Class clazz) throws Exception{
        
        Enhancer enhancer = new Enhancer();
        //把父類設置爲誰?
        //這一步就是告訴cglib,生成的子類需要繼承哪個類
        enhancer.setSuperclass(clazz);
        //設置回調
        enhancer.setCallback(this);
        
        //第一步、生成源代碼
        //第二步、編譯成class文件
        //第三步、加載到JVM中,並返回被代理對象
        return enhancer.create();
    }
    
    //同樣是做了字節碼重組這樣一件事情
    //對於使用API的用戶來說,是無感知
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) 
throws Throwable {
        System.out.println("我是GP媒婆:" + "得給你找個異性才行");
        System.out.println("開始進行海選...");
        System.out.println("------------");
        //這個obj的引用是由CGLib給我們new出來的
        //cglib new出來以後的對象,是被代理對象的子類(繼承了我們自己寫的那個類)
        //OOP, 在new子類之前,實際上默認先調用了我們super()方法的,
        //new了子類的同時,必須先new出來父類,這就相當於是間接的持有了我們父類的引用
        //子類重寫了父類的所有的方法
        //我們改變子類對象的某些屬性,是可以間接的操作父類的屬性的
        proxy.invokeSuper(obj, args);
        System.out.println("------------");
        System.out.println("如果合適的話,就準備辦事");
        return null;
    }

}

具體被代理對象:

public class Moumoumou{
    public void findLove(){
        System.out.println("膚白貌美大長腿");
    }
}

測試類:

try {
            Moumoumou obj = (Moumoumou)new GPMeipo().getInstance(Moumoumou.class);
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }

總結

Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想代理沒有實現接口的類,就可以使用Cglib實現.

Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
Cglib包的底層是通過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.

Cglib子類代理實現方法:

  1. 需要引入cglib的jar文件,但是Spring的核心包中已經包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
  2. 引入功能包後,就可以在內存中動態構建子類
  3. 代理的類不能爲final,否則報錯
  4. 目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.

另外一個小的知識點:
Spring中使用AOP代理的時候會分爲JDK和Cglib兩種代理方式取決的方式就和上面的相關:

  • 當目標類對象有實現接口的採用JDK代理
  • 沒有實現接口,就是普通類的時候採用Cglib
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章