Java动态代理 ----- Retrofit框架底层实现

封面图.jpg

前言

  Java动态代理的意义:
  动态代理实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中。
  动态代理其实质就是依靠反射来实现的。

1. 静态代理和动态代理的区别

  关于静态代理和动态代理的区别,网上的说法众说纷纭,其实总结起来就是两个字 ----- 时机
  静态代理在 编译期 就已经决定好了。而动态代理在 运行期 才决定代理对象是怎么样的,当调用生成代理类的方法时,会触发invoke方法,可以在其中替换或者扩充方法,使其更加灵活。
  具体比较如下:

1. 静态代理

  interface Person {
      void introduce(Context context);
  }

  public static class Man implements Person {
      @Override
      public void introduce(Context context) {
          Toast.makeText(context, "Man Man Man !!!", Toast.LENGTH_SHORT).show();
      }
  }

  public static class Woman implements Person {
      @Override
      public void introduce(Context context) {
          Toast.makeText(context, "Woman Woman Woman !!!", Toast.LENGTH_SHORT).show();
      }
  }

2. 动态代理

  Class c1 = Man.class;
  Person person = (Person) Proxy.newProxyInstance(c1.getClassLoader(),
                c1.getInterfaces(),
                new MyInvocationHandler(c1));
  person.introduce(MainActivity.this);

  public class MyInvocationHandler implements InvocationHandler {
      Class c1;

      public MyInvocationHandler(Class c1) {
          this.c1 = c1;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          return method.invoke(c1.newInstance(), args);
      }
  }

2. 动态代理注意点

  动态代理的优点是 灵活、适宜扩展 。但是使用动态代理需要有几个注意点

  • 无法摆脱仅支持interface代理圈子
  • Proxy.newProxyInstance()参数问题
  • Proxy.newProxyInstance()的return值只可强转为所对应的接口类
  • 若被代理类为接口,则不可直接调用method.invoke()方法

1. 无法摆脱仅支持interface代理圈子
  若上述代码中Man类并没有集成Person类,则无法使用动态代理,否则会报如下错误

Java动态代理 ----- Retrofit框架底层实现_第1张图片
错误.png

2. Proxy.newProxyInstance()参数问题

Java动态代理 ----- Retrofit框架底层实现_第2张图片
Proxy.png

  如上图所示,Proxy类中newProxyInstance方法的第二个参数是接口Class类的集合。动态代理中,被代理类可分为两种: 接口、接口实现类 。因此针对这两种不同类型,传递的参数也是不同的。
Java动态代理 ----- Retrofit框架底层实现_第3张图片
接口传参.png

Java动态代理 ----- Retrofit框架底层实现_第4张图片
接口实现类传参.png

3. Proxy.newProxyInstance()的return值只可强转为所对应的接口类
  如上图所示,若将对应接口Person类使用其实现类Man类替换,则会报如下错误

Java动态代理 ----- Retrofit框架底层实现_第5张图片
错误.png

4. 若被代理类为接口,则不可直接调用method.invoke()方法

Java动态代理 ----- Retrofit框架底层实现_第6张图片
invoke.png

  若被代理类为接口,则会报如下错误。
Java动态代理 ----- Retrofit框架底层实现_第7张图片
错误.png

应用举例

  • Retrofit框架

1. Retrofit简介
  Retrofit与Okhttp共同出自于Square公司,Retrofit就是对Okhttp做了一层封装的网络请求框架。

2. Okhttp框架使用
  在研究Retrofit框架底层代码实现原理前,先了解下Okhttp的使用。关于Okhttp框架的使用如下图。

Java动态代理 ----- Retrofit框架底层实现_第8张图片
Okhttp框架.png

具体代码如下

  compile 'com.squareup.okhttp3:okhttp:3.8.0'
  compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
  Message message = new Message();
  /*获取OkHttpClient(Header文件)*/
  OkHttpClient client = getClient();
  /*POST+键值对*/
  //RequestBody body = new FormBody.Builder()
  //.add("type", "android").build();
  /*POST+Json*/
  JSONObject json = new JSONObject();
  json.put("type", "android");
  RequestBody body = RequestBody.create(JSON, json.toString());

  Request request = new Request.Builder()
                .url("http://172.18.1.188/banner/fetch")
                .post(body)
                .build();
  Response response = client.newCall(request).execute();
  if (response.isSuccessful()) {
      message.what = 0;
      message.obj = response.body().string();
  } else {
      message.what = 1;
      message.obj = response.body().string();
  }
  myHandler.sendMessage(message);
  private OkHttpClient getClient() {
      HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
      interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
      OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .addInterceptor(new Interceptor() {
                           @Override
                           public Response intercept(Chain chain) throws IOException {
                               Request original = chain.request();
                               Request request = original.newBuilder()
                                             .header("Accept", "application/json")
                                             .build();
                               return chain.proceed(request);
                            }
                    })
                    .addInterceptor(interceptor)
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS);
      return builder.build();
  }

简单解释下整个Okhttp请求组成,主要分为两个类:

  • OkHttpClient
  • Request
  • OkHttpClient: 主要负责 Header头文件、Log信息各种超时时间(连接时间、读/写时间)等信息的封装。
  • Request: 主要负责 url、RequestBody(GET请求直接参数直接拼接在url上) 等信息的封装。其中,RequestBody封装参数信息,参数类型大致分为 键值对、Json格式 ,不同类型实例化方式不同。
  • Response: 最终产物。通过OkHttpClient.newCall()会生成 Call 对象,再通过Call对象调用execute()方法最终生成 Response对象。

3. Retrofit框架底层实现

Java动态代理 ----- Retrofit框架底层实现_第9张图片
Retrofit框架.png

   Retrofit + Okhttp 框架在Okhttp基础上进行了一层封装。如上图所示,关于 OkHttpClient 的配置依然由外部配置,通过Retrofit提供接口方法将对象传入。只是将 Request 配置由Retrofit内部来封装。
  Retrofit框架中的 create 方法是其中的核心方法,源码如下
Java动态代理 ----- Retrofit框架底层实现_第10张图片
Retrofit.png

  可以看到create方法唯一参数是一个接口的Class类(非接口实现类的Class类,因为由Proxy.newProxyInstance()方法的二参决定的,否则会报错,具体原因请向上翻),其内部使用的是就是 动态代理
  由上图所示,Retrofit进行动态代理封装时,所需要的几个重要参数有

  • OkHttpClient
  • url地址
  • 接口地址
  • 请求方式类型(GET、POST ......)
  • 参数类型(键值对、Json格式 ......)
  • 参数

  这几个重要参数在工程中体现在


Java动态代理 ----- Retrofit框架底层实现_第11张图片
Retrofit1.png

Java动态代理 ----- Retrofit框架底层实现_第12张图片
Retrofit2.png

  总结:urlOkhttpClient 利用Retrofit提供的 baseUrl() 和 client() 两个方法传入。开发者创建自己的Service接口,方法参数中包含网络请求所需要的 参数类型参数 ,方法的注解中包含网络请求的 请求类型接口地址

4. 自定义Retrofit框架
  在知道了Retrofit框架底层的实现原理,我们来手动的高仿去自定义一个自己的Retrofit框架(因为时间问题,这里只是针对Post请求和键值对参数,来搭建一个非常简陋的框架,模拟下Retrofit底层代码)。

  package com.test.administractor.proxy;

  import java.lang.reflect.Proxy;

  import okhttp3.OkHttpClient;

  /**
   * Created by Administrator on 2018/1/30 0030.
   */

  public class QqjRetrofit {
      private static QqjRetrofit qqjRetrofit;
      private MyInvocationHandler myInvocationHandler;
  
      public static QqjRetrofit getInstance() {
          if (qqjRetrofit == null) {
              qqjRetrofit = new QqjRetrofit();
          }
          return qqjRetrofit;
      }

      public QqjRetrofit init(String baseUrl, OkHttpClient okHttpClient) {
          myInvocationHandler = new MyInvocationHandler(baseUrl, okHttpClient);
          return this;
      }

      public  T create(Class service) {
          return (T) Proxy.newProxyInstance(service.getClassLoader(),
                  new Class[]{service},
                  myInvocationHandler);
      }
  }
  package com.test.administractor.proxy;

  import org.json.JSONObject;

  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.util.Iterator;
  import java.util.Map;

  import okhttp3.MediaType;
  import okhttp3.OkHttpClient;
  import okhttp3.Request;
  import okhttp3.RequestBody;

  /**
   * Created by Administrator on 2018/1/30 0030.
   */

  public class MyInvocationHandler implements InvocationHandler {
      public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
      private String baseUrl;
      private OkHttpClient okHttpClient;

      public MyInvocationHandler(String baseUrl, OkHttpClient okHttpClient) {
          this.baseUrl = baseUrl;
          this.okHttpClient = okHttpClient;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          /*POST+Json*/
          JSONObject json = new JSONObject();
          String url = (String) args[0];
          Map map = (Map) args[1];
          Iterator> it = map.entrySet().iterator();
          while (it.hasNext()) {
              Map.Entry entry = it.next();
              json.put(entry.getKey(), entry.getValue());
          }
          RequestBody body = RequestBody.create(JSON, json.toString());
          Request request = new Request.Builder()
                  .url(baseUrl + url)
                  .post(body)
                  .build();
          return okHttpClient.newCall(request);
      }
  }

主程序调用

  private void proxy() {
      new Thread(new Runnable() {
          @Override
           public void run() {
               Message message = new Message();
               try {
                   Map map = new HashMap();
                   map.put("type", "android");
                   TestClass testClass = QqjRetrofit.getInstance()
                                 .init("http://172.18.1.188", getClient())
                                 .create(TestClass.class);
                   Call call = testClass.getMessage("/banner/fetch", map);
                   Response response = call.execute();
                   if (response.isSuccessful()) {
                        
                   } else {
                        
                   }
              } catch (IOException e) {
                   e.printStackTrace();
              }
         }        
     }).start();
  }

  private OkHttpClient getClient() {
      HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
      interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
      OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request original = chain.request();
                            Request request = original.newBuilder()
                                          .header("Accept", "application/json")
                                          .build();
                            return chain.proceed(request);
                        }
                    })
                    .addInterceptor(interceptor)
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS);
      return builder.build();
  }

你可能感兴趣的:(Java动态代理 ----- Retrofit框架底层实现)