Retrofit 知識梳理(2) Retrofit 動態代理內部實現

1、前言

Retrofit 知識梳理(1) - 流程分析 中,咱們對於Retrofit的流程進行了簡單的分析,你們談到Retrofit的時候,每每也會提到動態代理,今天這篇文章,咱們就來一塊兒研究一下這一過程的內部實現。java

2、內部實現

涉及到動態代理的部分爲下面這兩句:數組

//1.返回代理對象
GitHubService service = retrofit.create(GitHubService.class);
//2.調用該代理對象的接口方法
Call<ResponseBody> call = service.listRepos("octocat");
複製代碼

2.1 生成代理對象

第一步的目的就是經過Retrofitcreate方法,根據GithubService這個接口所聲明的方法來獲得一個代理對象:bash

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  public <T> T create(final Class<T> 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, Object[] args)
              throws Throwable {
            //...

            //1.獲得ServiceMethod對象.
            ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);

            //2.經過serviceMethod和args來構建OkHttpCall對象,args就是「octocat」.
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

            //3.其實就是調用ExecutorCallbackCall.adapt方法.
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
複製代碼

能夠看到,這裏面最核心的方法就是:網絡

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
複製代碼

以上三個形參的含義爲:ide

  • ClassLoader:表示由哪個ClassLoader來加載這個代理類。
  • Class<?>[]:一個interface對象的數組,代表將要給被代理的對象提供一組什麼樣的接口來訪問,這個可能比較抽象。從上面的例子來講,咱們傳入的是GitHubService.class,而它是一個接口,其方法包含有listRepos,那麼Proxy.newProxyInstance所生成的代理對象也會有這個接口listRepos,因此咱們才能在第二步當中調用它的方法。
  • InvocationHandler:當調用代理對象的接口方法時,最終會回調到這個InvocationHandler對象的invoke方法中。

在對形參進行了簡要的介紹以後,咱們再來看一下其內部的實現:函數

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        if (h == null) {
            throw new NullPointerException();
        }
        //1.根據前兩個參數建立一個代理類.
        Class<?> cl = getProxyClass0(loader, interfaces);
        try {
            //2.實例化該代理類,並將第三個參數做爲成員變量.
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            return newInstance(cons, h);
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }
複製代碼

能夠看到,生成代理對象又能夠細分爲兩步:ui

  • 根據類加載器和聲明的接口,首先建立一個代理類,該代理類繼承於Proxy,而且它實現了interfaces數組中所聲明的接口
  • 實例化第一步中建立的代理類,並將第三個參數所對象的InvocationHandler傳入做爲其成員變量h

2.1.1 建立代理類

建立代理類的代碼比較多,也就是對應於getProxyClass0方法:this

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //最終要建立的代理類
        Class<?> proxyClass = null;
        String[] interfaceNames = new String[interfaces.length];
        Set<Class<?>> interfaceSet = new HashSet<>();
        //1.遍歷傳入的接口數組
        for (int i = 0; i < interfaces.length; i++) {
            String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(
                    interfaces[i] + " is not visible from class loader");
            }
            //必須是接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            //不容許重複
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }
        //2.若是以前已經生成過,那麼就不須要再去生成.
        List<String> key = Arrays.asList(interfaceNames);
        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {
            cache = loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
        }
        synchronized (cache) {
            do {
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class<?>) ((Reference) value).get();
                }
                if (proxyClass != null) {
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                    }
                    continue;
                } else {
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }
        //3.真正的產生操做.
        try {
            String proxyPkg = null;
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                proxyPkg = "";
            }

            {
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

                final long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                proxyClass = generateProxy(proxyName, interfaces, loader, methodsArray,
                        exceptionsArray);
            }

            proxyClasses.put(proxyClass, null);

        } finally {
            synchronized (cache) {
                if (proxyClass != null) {
                    cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {
                    cache.remove(key);
                }
                cache.notifyAll();
            }
        }
        return proxyClass;
    }
複製代碼

2.1.2 實例化代理類

當建立完代理類以後,接下來就是進行實例化,這裏實例化調用的是參數爲InvocationHandler的構造函數:spa

protected Proxy(InvocationHandler h) {
     this.h = h;
}
複製代碼

也就是說,最後經過Proxy.newProxyInstance返回的代理類實例,其內部的InvocationHandler對象,就是經過第三個參數所傳入的對象,而當咱們調用代理類所聲明的方法時,最終會調用到該InvocationHandlerinvoke方法,經過invoke方法的參數,咱們又能夠區分具體調用的方法。代理

2.2 調用代理對象的接口方法

經過第一步,咱們就能夠獲得一個代理對象,而當咱們調用這個代理對象的接口方法以後,該代理對象又會回調它的基類Proxy中的這個方法:

private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable {
    InvocationHandler h = proxy.h;
    return h.invoke(proxy, method, args);
}
複製代碼

invoke的三個參數解釋爲:

  • Proxy:代理對象實例,也就是上面在2.1中所返回的實例對象。
  • Method:代理對象所被調用的對應接口方法。
  • Object[]:接口方法所傳入的參數。

3、示例

在第二節當中,咱們對於源碼進行了簡單的走讀,下面,咱們以一個具體的示例來強化一下認識:

3.1 實例代碼

** (1) 接口類**

對應於咱們在上面所定義的GitHubService.java

public interface Subject {
    public CallObject getCallObject(String args);
}
複製代碼

(2) 請求類

對應於最終發起網絡請求的類:

public class CallObject {

    private String args;

    public CallObject(String args) {
        this.args = args;
    }

    public void call() {
        Log.d("simulate", "call args=" + args);
    }
}
複製代碼

(3) 模擬代理過程

public class Simulator {

    public static void simulate() {
        Class mClass = Subject.class;
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                mClass.getClassLoader(),
                new Class[]{ mClass },
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String modifiedArgs = (String) args[0] + " after modified";
                        Log.d("simulate", "invoke");
                        return new CallObject(modifiedArgs);
                    }

                });
        Log.d("simulate", "createProxy finished");
        CallObject callObject = proxySubject.getCallObject("args");
        Log.d("simulate", "getCallObject finished");
        callObject.call();
    }
}
複製代碼

3.2 示例詳解

按照咱們以前的分析,在上面的模擬過程當中的第三步,會返回一個代理類,咱們來看一下這個代理類具體是什麼:

經過斷點,咱們能夠看到它再也不是咱們以前聲明的接口,而是一個代理類 $Proxy,它的內部有一個 InvocationHandler的實現類 h,也就是咱們經過 newProxyInstance傳入的實例。

以後,咱們調用該代理類的接口方法,那麼上面的h對象的invoke方法的就被回調,咱們能夠從中獲取到傳入的參數、註解等信息,經過該信息,咱們構造出一個CallObject對象。

最後,再經過這個CallObject發起請求:

4、總結

普通的代理模式,每每會在InvocationHandler的實現類中包含一個接口的實現類,當該InvocationHandlerinvoke方法被回調時,再調用他所包含的接口實現類進行操做,並在以前和以後進行一些處理的操做。對於標準的動態代理,能夠參考 這篇文章

Retrofit用到的動態代理,並不能算是嚴格的代理模式。它只是利用了代理模式中invoke這一中轉過程,來解析接口中的註解聲明,而後經過這些註解聲明來建立一個請求類,最終再經過該請求類來發起請求。

也就是說,Retrofit所關注的重點在於如何建立invoke方法所返回的實例,而普通的代理模式則在於控制接口實現類的訪問。

相關文章
相關標籤/搜索