Retrofit与动态代理

2019-05-22 23:04:22 浏览数 (1)

什么是代理

代理,即Proxy。它的作用就是将原数据与后续的操作进行隔离,达到对修改封闭,对扩展开放的效果。

从现实生活中理解,厂商生产产品到代理商,而代理商负责找渠道销售产品。期间,厂商与销售人员不会有任何接触,也不管销售效果如何。

什么是静态代理

在Java中,通常会用代理模式来完成一些额外的操作。

例如,有一件商品,售价为25元,但是经过代理商之后,售价要提高5元。

代码语言:javascript复制
// 定义货物的接口,以及获取价格的函数
public interface Cargo{
      public int getPrice();
}

// 定义产品,实现Cargo接口,返回25元
public class Product implements Cargo{
    @Override
    public int getPrice(){
        return 25;
    }
}

// 定义产品的代理,实现Cargo接口,并且在原有的基础上增加5元再进行销售
public class ProductProxy implements Cargo{
     private Product product;

     public ProductProxy(Product product){
           this.product = product;
     }

    @Override
    public int getPrice(){
        return product.getPrice() 5;
    } 
}
静态代理的优点
  • 隐藏委托类的实现,保证委托类的独立
  • 实现代理与委托类之间的解耦,不侵入委托类的代码

动态代理的背景

在使用静态代理的过程中,会产生如下问题:

  • 大型项目的复杂度,如果代理过多的话,会导致维护成本很大,并且难以理解
  • 通过接口实现的静态代理作用死板,对于功能的复用有很大影响

举例,我们希望统计产品在生产过程中(原材料采购 --> 材料加工 --> 产品制作 --> 后期包装等步骤)消耗的时间,然后产生报表。如果还使用静态代理的话,则这个代理类中都是相同的代码,并且后续再添加接口,仍然需要修改该代理类

于是,Java提出了动态代理的概念。

动态代理

动态代理,也就是在运行时创建的代理类。在运行过程中,会在虚拟机内部创建一个Proxy的类。通过实现InvocationHandler的接口,来代理委托类的函数调用。

代码语言:javascript复制
public class ProductProxy implements InvocationHandler{

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

还是以上述商品来举例。

代码语言:javascript复制
 public class ProductProxy implements InvocationHandler{
         // 被委托的对象
         private Product product;

         public ProductProxy(Product product){
                 this.product = product;
          }

        @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             if(method.getName().equals("getPrice")){
                // 如果调用的函数为getPrice的话,则 5
                  return method.invoke(product,args) 5;
             }
            return null;
        }
    }

public static void main(String []args){
     // 创建代理类
     ProductProxy proxy = new ProductProxy(new Product());
     //获取代理类实例Product
     Product product = (Product)(Proxy.newProxyInstance(Product.class.getClassLoader(), new Class[] {Product.class}, proxy)); 
     // 调用getPrice的函数,就会走到动态代理的invoke函数中
     product.getPrice();
}

通过动态代理,我们可以通过函数名来判断对应的函数以及对应操作,甚至于修改参数。

Retrofit中的动态代理

在Retrofit中,使用动态代理来对接口中的注释进行解析,解析后完成OkHttp的参数构建。

Retrofit的基本使用

首先来看一下使用Retrofit请求Github的的代码

  • 通过interface以及注释定义了该为Get请求,并且路径为/
代码语言:javascript复制
public interface GitHubService {
        @GET("/")
        Call<GitHubApiBean> listGitHubApis();
}
  • 定义baseUrl为域名,并且添加GsonConverterFactory作为Response的转换工厂,创建Retrofit对象
代码语言:javascript复制
String url = "https://api.github.com/";
Retrofit retrofit = new Retrofit.Builder()
       .baseUrl(url)//url必须以‘/’结尾
       .addConverterFactory(GsonConverterFactory.create())
       .build();
  • 调用函数,开始发起请求
代码语言:javascript复制
//retrofit.create来生成一个接口实现类
GitHubService gitHubService=retrofit.create(GitHubService.class);
//调用指定方法
Call<GitHubApiBean> gitHubBeanCall=gitHubService.listGitHubApis();
//执行请求
gitHubBeanCall.enqueue(new Callback<GitHubApiBean>() {
     @Override
     public void onResponse(Call<GitHubApiBean> call, Response<GitHubApiBean> response) {
      String authorizations_url= response.body().getAuthorizations_url();
       String team_url= response.body().getTeam_url();
     }

    @Override
    public void onFailure(Call<GitHubApiBean> call, Throwable t) {
                Log.i("zfq", t.getMessage());
            }
    });

原理分析

在Retrofit的create中会通过Proxy.newProxyInstance来为传入的Service接口类创建一个代理对象,而当代理对象调用函数时,会调用动态代理的invoke函数。

代码语言:javascript复制
public <T> T create(final Class<T> service) {
    ...
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
         
          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            ...
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

而在invoke函数中,会通过调用loadServiceMethod函数,对Method中的注释进行解析,并且返回ServiceMethod对象,传入OkHttpCall中,构建OkHttp的请求。

代码语言:javascript复制
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      ...
      // 通过注释中的Converter,创建Response中的转换器
      responseConverter = createResponseConverter();
      for (Annotation annotation : methodAnnotations) {
        // 解析注释中的GET/Post、Multipart等等
        parseMethodAnnotation(annotation);
      }
      ...  
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p  ) {
        Type parameterType = parameterTypes[p];
               Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
      ...
    // 解析函数参数中的注释,例如Query,PartMap,FieldMap等
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      ...
      return new ServiceMethod<>(this);
    }

Retrofit中的CallAdapter

CallAdapter的作用则是将请求中的数据进行转换,通过adapt函数进行转换,将A转换成B类型。

代码语言:javascript复制
Retrofit.Builder test =new Retrofit.Builder().addCallAdapterFactory(new CallAdapter.Factory() {
            @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
                return new CallAdapter<Object, Object>() {
                    @Override public Type responseType() {
                        return null;
                    }

                    @Override public Object adapt(Call<Object> call) {
                        return null;
                    }
                };
            }
        });

0 人点赞