Retrofit分析

Retrofit

深入分析Java ClassLoader原理
而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。

双亲委派模型
双亲委派模型过程:某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。

双亲委派模型的系统实现

在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自


JDK中的proxy动态代理原理剖析

主要几个方法

/*
 * Look up or generate the designated proxy class.
 */
private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces)//Proxy
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces)//ProxyClassFactory
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2)//ProxyGenerator
 private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);//Proxy
 cons.newInstance(new Object[]{h})
  1. 代理类的class样本
package com.sun.proxy;

import com.czq.proxy.IPackageManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements IPackageManager
{
  private static Method m3; // 生成对应的方法对象
  private static Method m1;
  private static Method m0;
  private static Method m2;
// proxy0 继承Proxy,实现IPackageManager 接口,需要传入 InvocationHandler,初始化对应的h对象。
// 我们的h对象就是PackageManagerWoker,所以我们会调用到PackageManagerWoker的 invoke方法。
// 所以是proxy0,调用InvocationHandler的 invoke 方法,传入对应的方法。InvocationHandler 放射调用对应的tagret中的方法。
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws
  {
    super(paramInvocationHandler);
  }

  public final String getPackageInfo()
    throws
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final boolean equals(Object paramObject)
    throws
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
     // 把各个方法,对应到成员变量上
      m3 = Class.forName("com.czq.proxy.IPackageManager").getMethod("getPackageInfo", new Class[0]);
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    }
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}


  1. newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)调用后会生成上面一个class文件,可以看到上面的文件实现了interfaces中的接口即IPackageManager接口,除了实现接口中的方法getPackageInfo() 还默认实现了toString,equals,hashcode的方法。还有一个InvocationHandler 成员。

  2. loader就是用于从class文件中加载class用的,通过cl.getConstructor(constructorParams)及 cons.newInstance(new Object[]{h})实例化的到Proxy0对象并把h传入。

  3. 最后就是调用Proxy0这个对象的方法,可以看到每次都是去调用h.invoke方法,这就是我们要在

    InvocationHandler的invoke方法下写响应逻辑的原因.

  4. 每调用newProxyInstance会以interfaces生成一个key,Proxy0对象做为一个值存入map中

    下一次同样调用直接从map中取出.


retrofit是一个用注解修饰方法来定义如何去做一个http请求。

image-20181128113516304.png
  1. args到request放在请求这步是由于args是变的 而Method其他注解返回值类型是不变的。其中有一个 Map<Method, ServiceMethod<?>> serviceMethodCache缓存。

  2. 不同的 ParameterHandler上有不同的Converter: Converter<?, RequestBody>,Converter<?, String> stringConverter。用于区分转换。ParameterHandler也是为了解决第一个问题吧

  3. 需要直接使用ResposeBody的source 需要将resposeType==ResposeBody.这样的话BuiltInConverters 会去拦截converter。

  4. Method反射这块没暂时没深入 写了Test

    Observable<List<String>> test = ((Inner) Proxy.newProxyInstance(Inner.class.getClassLoader(), new Class<?>[] { Inner.class }, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Class<?> declaringClass = method.getDeclaringClass();//interface retrofit2.adapter.rxjava.ZhouTest$Inner
            Type genericReturnType = method.getGenericReturnType();//rx.Observable<java.util.List<java.lang.String>>
            Class<?> returnType = method.getReturnType();//class rx.Observable
            Annotation[] annotations = method.getAnnotations();//@retrofit2.http.HEAD(value=test head),@retrofit2.http.GET(value=test get)
            //{Annotation[1][]} @retrofit2.http.Path(encoded=false, value=test path),@retrofit2.http.Query(encoded=false, value=test qury )
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            return Observable.just("test");
        }
    })).justTest("test");
    
    
     interface Inner{
            @HEAD("test head")
            @GET("test get")
            Observable<List<String>> justTest(@Path("test path")@Query("test qury ")String test);
        }
    

最后理一下下面这些参数作用。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build();
  • client 传入的的是OkhttpClient。和callFactory()功能是一样。用于创建一个正在的调用网络请求的Call

    OkhttpClient中是RealCall。

  • Converter.Factory 用于创建responseBodyConverter,requestBodyConverter,stringConverter

    分别用于返回时ResponseBody的转换,请求时转换成RequestBody和转为String

  • CallAdapter.Factory用于创建CallAdapter创建时会传入一个returnType用于确定responseType.

    responseBodyConverter需要转换为啥就拿的这个参数。CallAdapter的作用就是设配如何去调用Call。

比如Rxjava,subscibe就会去调用Call

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,200评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,526评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,321评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,601评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,446评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,345评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,753评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,405评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,712评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,743评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,529评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,369评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,770评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,026评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,301评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,732评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,927评论 2 336

推荐阅读更多精彩内容