Android 靜態代理與動態代理詳解

學習代理前,咱們先了解代理的含義

  1. 定義:給目標對象提供一個代理對象,並由代理對象控制目標對象的引用
  2. 目的:經過引入代理的方式來間接訪問目標對象,防止直接訪問目標對象給系統帶來不肯定的複雜性

爲何會有代理出現?

在傳統的面向對象思想中,若是想要實現功能複用,要麼繼承,要麼引用,不管哪一種方式,對代碼都有必定的侵入性,耦合無可避免java

侵入性含義:

若是你想用它加強你程序的功能,你必須改動你的程序代碼,那它就具備侵入性! 若是隻有一兩點須要加強還好說,但若是大量功能點須要被加強,那麼工做量就會很大,代碼也不優雅! 想象一下,若是你對外公開了一系列接口,忽然領導說了,接口要加權限控制。在哪加? 最笨的固然是寫個程序驗證邏輯,而後每一個接口都拿來調用一遍。 這也是面向對象的思想短板,在要爲程序新增一些通用功能時,只能經過耦合的方式才能進行。 而代理(動態代理)就能很好的解決該問題!git

靜態代理與動態代理

根據加載被代理類的時機不一樣,將代理分爲靜態代理和動態代理。

  • 靜態代理:編譯時就肯定了被代理的類是哪個
  • 動態代理:運行時才肯定被代理的類是哪一個

靜態代理使用

一、靜態代理需實現的方法

public interface Subject {

    void sayGoodBye();

    void sayHello(String str);

    boolean isProxy();
}

複製代碼

二、定義被代理類(原來功能類)並實現被代理類的功能邏輯(打死都不改的那種)

public class RealSubject implements Subject {
    @Override
    public void sayGoodBye() {
        System.out.println("RealSubject 我是原封不動的代碼 sayGoodBye ");
    }
    @Override
    public void sayHello(String str) {
        System.out.println("RealSubject 我是原封不動的代碼 sayHello " + str);
    }

    @Override
    public boolean isProxy() {
        System.out.println("RealSubject 我是原封不動的代碼 isProxy ");
        return false;
    }
}

複製代碼

三、定義靜態代理類(功能增長類),這個代理類也必需要實現和被代理類相同的Subject接口,便於對原有功能的加強

public class ProxySubject implements Subject {
    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void sayGoodBye() {
        //代理類,功能的加強 調用前 sayGoodBye 可作操做(好比是否能調用的權限認證)
        System.out.println("ProxySubject sayGoodBye begin " +
                "代理類,功能的加強 調用前 sayGoodBye 可作操做(好比是否能調用的權限認證)");
        //在代理類的方法中 間接訪問被代理對象的方法
        subject.sayGoodBye();
        System.out.println("ProxySubject sayGoodBye end " +
                "這裏可處理原方法調用後的邏輯處理");
    }

    @Override
    public void sayHello(String str) {
        //代理類,功能的加強 調用前 sayHello 可作操做(好比是否能調用的權限認證) 並測試帶參數的方法
        System.out.println("ProxySubject sayHello begin " +
                "代理類,功能的加強 調用前 sayHello 可作操做(好比是否能調用的權限認證)");
        //在代理類的方法中 間接訪問被代理對象的方法
        subject.sayHello(str);
        System.out.println("ProxySubject sayHello end " +
                "這裏可處理原方法調用後的邏輯處理");
    }

    @Override
    public boolean isProxy() {
        //代理類,功能的加強 調用前 isProxy 可作操做(好比是否能調用的權限認證) 並測試帶返回的方法
        System.out.println("ProxySubject isProxy begin " +
                "代理類,功能的加強 調用前 isProxy 可作操做(好比是否能調用的權限認證)");
        boolean boolReturn = subject.isProxy();
        System.out.println("ProxySubject isProxy end " +
                "這裏可處理原方法調用後的邏輯處理");
        return boolReturn;
    }
}
複製代碼

四、靜態代理使用

public class ProxyMain {

    public static void main(String[] args) {
        //被代理的對象,某些狀況下 咱們不但願修改已有的代碼,咱們採用代理來間接訪問
        RealSubject realSubject = new RealSubject();
        //代理類對象,將原有代碼不想修改的對象傳入代理類對象
        ProxySubject proxySubject = new ProxySubject(realSubject);
        //調用代理類對象的方法
        proxySubject.sayGoodBye();
        System.out.println("******");
        proxySubject.sayHello("Test");
        System.out.println("******");
        proxySubject.isProxy();
    }

}
複製代碼

五、最終打印

ProxySubject sayGoodBye begin  代理類,功能的加強 調用前 sayGoodBye 可作操做(好比是否能調用的權限認證)

RealSubject 我是原封不動的代碼 sayGoodBye 

ProxySubject sayGoodBye end 這裏可處理原方法調用後的邏輯處理

******

ProxySubject sayHello begin  代理類,功能的加強 調用前 sayHello 可作操做(好比是否能調用的權限認證)

RealSubject 我是原封不動的代碼 sayHello  Test

ProxySubject sayHello end 這裏可處理原方法調用後的邏輯處理

******

ProxySubject isProxy begin  代理類,功能的加強 調用前 isProxy 可作操做(好比是否能調用的權限認證)

RealSubject  我是原封不動的代碼    isProxy  

ProxySubject isProxy end 這裏可處理原方法調用後的邏輯處理   
複製代碼

六、總結:

靜態代理(傳統代理模)的實現方式比較暴力直接,須要將全部被代理類的全部方法都寫一遍,而且一個個的手動轉發過去,麻煩並繁瑣。因此咱們要學習並使用動態代理。github

動態代理核心原理

一、在java的動態代理機制中,有兩個重要的類或接口

  • 一個是 InvocationHandler(Interface) 須要代碼裏須要實現該接口
  • 一個則是Proxy(Class)

二、InvocationHandler (Interface) 詳解

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
複製代碼
  • proxy:指代生成的代理對象;
  • method:指代的是咱們所要調用真實對象的某個方法的Method對象;
  • args:指代的是調用真實對象某個方法時接受的參數;
  • 返回值 Object :指的是須要原封不動的返回,被代理所調用的方法的返回

三、Proxy (Class) 核心原理

  • 編譯時,代理對象的class並不存在,當須要調用 Proxy.newProxyInstance 方法時,會構建一個Proxy0的class字節碼,而且加載到內存

四、Proxy.newProxyInstance方法詳解

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {....}
複製代碼
  • loader:一個ClassLoader對象,定義了由哪一個ClassLoader對象來對生成的代理對象進行加載
  • interfaces:一個Interface對象的數組,表示的是我將要給我須要代理的對象提供一組什麼接口,若是我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
  • 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個InvocationHandler對象上。

動態代理使用

第一步:定義一個接口 (待實現的邏輯)

public interface Subject {

    void sayGoodBye();

    void sayHello(String str);

    boolean isProxy();
}

複製代碼

第二步:定義真實對象(被代理類,打死都不改的那種):

public class RealSubject implements Subject {
    @Override
    public void sayGoodBye() {
        System.out.println("RealSubject 我是原封不動的代碼 sayGoodBye ");
    }
    @Override
    public void sayHello(String str) {
        System.out.println("RealSubject 我是原封不動的代碼 sayHello " + str);
    }

    @Override
    public boolean isProxy() {
        System.out.println("RealSubject 我是原封不動的代碼 isProxy ");
        return false;
    }
}

複製代碼

第三步:定義一個InvocationHandler, 至關於一個代理處理器

public class SubjectInvocationHandler implements InvocationHandler {
    //這個就是咱們要代理的真實對象
    private Object subject;

    //構造方法,給咱們要代理的真實對象賦初值
    public SubjectInvocationHandler(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        //在代理真實對象前咱們能夠添加一些本身的操做
        System.out.println("before Method invoke 代理類,功能的加強 調用前 "+method+" 可作操做(好比是否能調用的權限認證)");
        System.out.println("Method:" + method);
        //當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用,獲得對應的返回值,最後將對應返回值返回
        Object obj = method.invoke(subject, args);
        //在代理真實對象後咱們也能夠添加一些本身的操做
        System.out.println("after Method invoke 這裏可處理原方法調用後的邏輯處理 ");
        return obj;
    }
}
複製代碼

第四步:調用

public class ProxyMain {

    public static void main(String[] args) {


        //被代理類
        Subject realSubject = new RealSubject();
        //咱們要代理哪一個類,就將該對象傳進去,最後是經過該被代理對象來調用其方法的
        SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
        //生成代理類
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), handler);
        //輸出代理類對象
        System.out.println("Proxy : " + subject.getClass().getName());
        System.out.println("Proxy super : " + subject.getClass().getSuperclass().getName());
        System.out.println("Proxy interfaces : " + subject.getClass().getInterfaces()[0].getName());
        //調用代理類sayGoodBye方法
        subject.sayGoodBye();
        System.out.println("--------");
        //調用代理類sayHello方法
        subject.sayHello("Test");
        System.out.println("--------");
        System.out.println("---subject.isProxy()-----" + subject.isProxy());
    }

}
複製代碼

最終打印:

Proxy : com.sun.proxy.$Proxy0
Proxy super : java.lang.reflect.Proxy
Proxy interfaces : staticproxy.Subject
before Method invoke  代理類,功能的加強 調用前 public abstract void staticproxy.Subject.sayGoodBye() 可作操做(好比是否能調用的權限認證)
Method:public abstract void staticproxy.Subject.sayGoodBye()
RealSubject 我是原封不動的代碼 sayGoodBye 
after Method invoke  這裏可處理原方法調用後的邏輯處理 
--------
before Method invoke  代理類,功能的加強 調用前 public abstract void staticproxy.Subject.sayHello(java.lang.String) 可作操做(好比是否能調用的權限認證)
Method:public abstract void staticproxy.Subject.sayHello(java.lang.String)
RealSubject 我是原封不動的代碼 sayHello  Test
after Method invoke  這裏可處理原方法調用後的邏輯處理 
--------
before Method invoke  代理類,功能的加強 調用前 public abstract boolean staticproxy.Subject.isProxy() 可作操做(好比是否能調用的權限認證)
Method:public abstract boolean staticproxy.Subject.isProxy()
RealSubject  我是原封不動的代碼    isProxy     
after Method invoke  這裏可處理原方法調用後的邏輯處理 
---subject.isProxy()-----false
---subject.isProxy()-----false
複製代碼

動態代理總結

  • 動態代理可以增長程序的靈活度,好比調用方法先後的邏輯處理
  • 完美解決解耦問題,動態代理能夠將調用層和實現層分離
  • 動態代理不須要接口實現類
  • 動態代理能夠解決程序執行流程(下期講解事件轉到activity執行)

項目地址:點擊下載

相關文章
相關標籤/搜索