動態代理是基於什麼原理?

遛狗的程式設計師發表於2018-07-26

問題:

動態代理是基於什麼原理?

知識點:

  1. Java是靜態的強型別語言,但是因為提供了類似反射等機制,也具備了部分動態型別的語言能力。
  2. 代理模式: 定義:給某個物件提供一個代理物件,並由代理物件控制對於原物件的訪問,即客戶不直接操控原物件,而是通過代理物件間接地操控原物件。(類似在上海這邊經常看到的房屋中介)

image

靜態代理模式程式碼:

public class ProxyDemo {
    public static void main(String args[]){
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}

interface Subject{
    void request();
}

class RealSubject implements Subject{
    public void request(){
        System.out.println("request");
    }
}

class Proxy implements Subject{
    private Subject subject;
    public Proxy(Subject subject){
        this.subject = subject;
    }
    public void request(){
        System.out.println("PreProcess");
        subject.request();
        System.out.println("PostProcess");
    }
}


複製程式碼
  1. 反射: Java 反射機制在程式執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種 動態的獲取資訊 以及 動態呼叫物件的方法 的功能稱為 java 的反射機制。 反射機制很重要的一點就是“執行時”,其使得我們可以在程式執行時載入、探索以及使用編譯期間完全未知的 .class 檔案。換句話說,Java 程式可以載入一個執行時才得知名稱的 .class 檔案,然後獲悉其完整構造,並生成其物件實體、或對其 fields(變數)設值、或呼叫其 methods(方法)。具體可以參考:Java 反射由淺入深 | 進階必備

執行時註解利用反射會影響效能,編譯時候註解不會影響效能。 通過註解方式宣告訂閱方法,速度相比2.x會變慢,通過引入註解處理器(annotation processor),在編譯期間建立訂閱方法的索引,效能有明顯提升。

根據官方說明,註解的引入讓EventBus 3.0相比2.x效能變差3-5倍,但是引入索引,3.0相比2.x效能提高至少3倍。

詳細瞭解請參考:EventBus 3.0 最佳實踐和原理淺析

  1. 動態代理: 為其他物件提供一種代理以控制對這個物件的訪問。某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以再兩者之間起到中介作用。執行階段才指定代理哪個物件。 組成元素:
    • 抽象類介面
    • 被代理類(具體實現抽象類介面的類)
    • 動態代理類,實際呼叫被代理類的方法和屬性

實現方式 - [x] JDK 自身提供的動態代理,就是主要利用了反射機制

//retrofit2.Retrofit原始碼中就使用到了
public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T)
    //劃重點了
    Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
複製程式碼

其他實現方式:利用位元組碼操作機制,類似ASM、GGLB(基於ASM)、Javassist等

回答問題:

這裡不作回答了,自己根據上面知識點進行總結吧。在APM功能開發的時候,動態代理使用場景及其廣泛,以及在我們針對不同機型適配的時候,反射使用場景次數較多。

/**
     * 隱藏系統鍵盤
     */
    public void hintSystemSoftKeyboard() {
        if (getWindowToken() != null) {
            ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getWindowToken(), 2);
        }
        if (Build.VERSION.SDK_INT >= 11) {
            try {
            //以下是本章重點內容,請仔細體會
                Class[] arrayOfClass = new Class[1];
                arrayOfClass[0] = Boolean.TYPE;
                Method localMethod = null;
                if (Build.VERSION.SDK_INT < 17) {
                    localMethod = EditText.class.getMethod("setSoftInputShownOnFocus", arrayOfClass);
                } else {
                    localMethod = EditText.class.getMethod("setShowSoftInputOnFocus", arrayOfClass);
                }
                localMethod.setAccessible(true);
                Object[] arrayOfObject = new Object[1];
                arrayOfObject[0] = Boolean.valueOf(false);
                localMethod.invoke(this, arrayOfObject);
                return;
            } catch (SecurityException localSecurityException) {
                localSecurityException.printStackTrace();
                return;
            } catch (NoSuchMethodException localNoSuchMethodException) {
                localNoSuchMethodException.printStackTrace();
                return;
            } catch (Exception localException) {
                localException.printStackTrace();
                return;
            }
        }
        setInputType(0);
    }
複製程式碼

參考:

宣告:此為原創,轉載請聯絡作者


作者:微信公眾號新增公眾號-遛狗的程式設計師 ,或者可以掃描以下二維碼關注相關技術文章。

qrcode_for_gh_1ba0785324d6_430.jpg
當然喜愛技術,樂於分享的你也可以可以新增作者微訊號:

WXCD.jpeg

相關文章