美文网首页
代理模式,及在Retrofit中的使用

代理模式,及在Retrofit中的使用

作者: 瑜小贤 | 来源:发表于2020-04-03 14:06 被阅读0次

定义

给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用

目的

  • 通过引入代理对象的方式来简介访问目标对象,防止直接访问目标对象给系统带来的不必要的复杂性
  • 通过代理对象对原有的业务进行增强

静态代理及问题

public class Client {
    public static void main(String[] args) {
        //ManToolsFactoryInterface是接口,AToolsFactory是实体工厂,SalesmanAndy是代理
        ManToolsFactoryInterface aFactory = new AToolsFactory();
        SalesmanAndy andy = new SalesmanAndy(aFactory);
        andy.saleManTools("D");

        //WomanToolsFactoryInterface是接口,BToolsFactory是实体工厂,SalesmanBob是代理
        WomanToolsFactoryInterface bFactory = new BToolsFactory();
        SalesmanBob bob = new SalesmanBob(bFactory);
        bob.saleWomanTools(1.8f);
    }
}



public class AToolsFactory implements ManToolsFactoryInterface {
    @Override
    public void saleManTools(String size) {
        System.out.println("按需求定制了一个size为"+size+"的女model");
    }
}


public class SalesmanAndy implements ManToolsFactoryInterface {
    /*包含真实的对象*/
    public ManToolsFactoryInterface factory;

    public Mark(ManToolsFactoryInterface factory) {
        this.factory = factory;
    }

    /*前置处理器*/
    private void doSthAfter() {
        System.out.println("精美包装,快递一条龙服务");
    }

    /*后置处理器*/
    private void doSthBefore() {
        System.out.println("根据需求,进行市场调研和产品分析");
    }

    @Override
    public void saleManTools(String size) {
        doSthAfter();
        factory.saleManTools(size);
        doSthBefore();
    }
}

  • 每来一个需求,就得生成一个代理类对象(SalesmanAndy、SalesmanBob)
  • 代理类对象持有目标对象(factory),当接口发生变化,修改维护变得复杂

违反开闭原则,导致

  • 拓展性差
  • 可维护性差

动态代理及问题

public class MarkCompany implements InvocationHandler {
    /*持有的真实对象*/
    private Object factory;

    public Object getFactory() {
        return factory;
    }

    public void setFactory(Object factory) {
        this.factory = factory;
    }

    /*通过Proxy获得动态代理对象*/
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(), this);
    }

    /*通过动态代理对象方法进行增强*/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        doSthAfter();
        Object result = method.invoke(factory, args);
        doSthBefore();
        return result;
    }

    /*前置处理器*/
    private void doSthAfter() {
        System.out.println("精美包装,快递一条龙服务");
    }
    /*后置处理器*/

    private void doSthBefore() {
        System.out.println("根据需求,进行市场调研和产品分析");
    }
}

public class Client {
    public static void main(String[] args) {
        ManToolsFactory aFactory = new AToolsFactory();
        MarkCompany markCompany = new MarkCompany();
        markCompany.setFactory(aFactory);

        //张三来了
        ManToolsFactoryInterface employee1 = (ManToolsFactoryInterface)markCompany.getProxyInstance();
        employee1.saleManTools("E");

        //张三老婆来了
        WomanToolsFactoryInterface bToolsFactory = new BToolsFactory();
        markCompany.setFactory(bToolsFactory);
        WomanToolsFactoryInterface employee2
                = (WomanToolsFactoryInterface)markCompany.getProxyInstance();
        employee2.saleWomanTools(1.9f);

        //动态代理,不出现代理类、代理对象,运行时出现
    }
}

  • 需要使用到反射,效率比静态代理低

  • 受限:因为java单继承,所以只能针对接口创建代理类,不能针对类创建代理类

  • cglib 后端Spring使用比较多

  • 重点类:Proxy(类比有资源的代购人) InvocationHandler(优质的代购服务,包括售前 购买 售后)


对于动态代理类来说,没有编写Java源文件这一步骤

注:代理模式中 代理类-代理对象和实体类-真实对象都需要实现需要被代理的接口

Retrofit中的动态代理

了解Retrofit用动态代理解决了什么问题。

下面代码是OkHttp一个GET请求实例

  //第一步:初始化
  OkHttpClient client = new OkHttpClient();
  //第二步获取请求对象
  Request request = new Request.Builder()
      .url(url)
      .build();
  //执行请求获取服务器响应对象
  Response response = client.newCall(request).execute();
  response.body().string();

再看Retrofit如何发起请求的

//第一步,初始化,配置需要的物料,例如请求地址的根路径,转化工厂,拦截器什么的
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

// Create an instance of our GitHub API interface.
//第二步使用动态代理创建代理类对象
GitHub github = retrofit.create(GitHub.class);

// Create a call instance for looking up Retrofit contributors.
// 然后调用代理类的方法获取请求对象
Call<List<Contributor>> call = github.contributors("square", "retrofit");

// Fetch and print a list of the contributors to the library.
//第三步 执行请求获取服务器响应对象
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
    System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

都是3步走

  • 初始化
  • 创建请求对象
  • 发送请求获取数据.

Retrofit用这两行替代了OkHttp的这两行

GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");

public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo);
}
String url ="baseUrl"+/repos/"+"square"+"/"+"retrofit"+"/contributors"
Request request = new Request.Builder()
      .url(url)
      .build();
Response response = client.newCall(request).execute();
String result=response.body().string()
return GsonUtlis.from(result);

其实优势已经出来了,okhttp url的拼接,是由我们来做,body的数据是字符串或者其它,还要手动处理成gson或者其它,这还只是get请求。创建请求对象的时候还要创建请求体,但是Retrofit就更简单,按照暴露的接口,传参就好了,帮你返回你想要对象,你什么都不用做,只用处理传入参数,获取结果。实现了一个黑盒。

了解Retrofit是如何做到的

如何动态产生请求对象Request,弄明白这两行我们就算大功告成了.

GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");
  1. 首先定义了一个接口,让用户告诉我,你是什么请求,传了那些参数,想获得什么对象.
public interface GitHub {
        @GET("/repos/{owner}/{repo}/contributors")
        Call<List<Contributor>> contributors(
                @Path("owner") String owner,
                @Path("repo") String repo);
    }
  1. 拿到这个接口,处理这个数据,如何通过接口获取数据呢?
    下面看Retrofit源码
public <T> T create(final Class<T> service) {
    // 删除一些校验接口合法性的代码
    //第一个参数类加载器,第二个要代理的接口对象,第三个代理对象执行方法时的回调
    return (T) Proxy.newProxyInstance(
        service.getClassLoader(), 
        new Class<?>[] { service },
        new InvocationHandler() {
            //获取用户哪个平台,有android和java8
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
                  if(method.getDeclaringClass() == Object.class){
                      return method.invoke(this, args);
                  }
                  
                  if(platform.isDefaultMethod(method)){
                      return platform.invokeDefaultMethod(method, service, proxy, args);
                  }
                  return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

这里最核心的就是loadServiceMethod(method)获取被代理对象,这里你可要注意,这里loadServiceMethod返回的是ServiceMethod

ServiceMethod<?> loadServiceMethod(Method method) {
    //从缓存中获取,因为重新获取一遍还是挺麻烦的
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);//这里又获取了一次
      if (result == null) {
        //生成ServiceMethod的地方
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

上面主要看的是 ServiceMethod.parseAnnotations(this, method);

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

上面主要看的是HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);

继续追踪到HttpServiceMethod

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType = Utils.getParameterLowerBound(0,
          (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
        // TODO figure out if type is nullable or not
        // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
        // Find the entry for method
        // Determine if return type is nullable or not
      }

      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
  }

create方法里的return语句中,就返回了上面的HttpServiceMethod,并调用了下面的invoke方法

@Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

这里就获得了咱们的 Call<List<Contributor>> call,至于adapter()方法的后续追踪,这里没有详细展开去分析,因为这里和我们动态代理没有关系了。

相关文章

  • 代理模式,及在Retrofit中的使用

    定义 给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用 目的 通过引入代理对象的方式来简介访问目标对象...

  • Retrofit的使用之设计模式

    使用的设计模式有: 动态代理 门面模式 适配器模式创建流程创建流程 创建Retrofit 首先构造retrofit...

  • 搞定代理模式,看懂Retrofit动态代理的骚操作

    代理模式 看完文章你能学到什么?搞懂代理模式,Retrofit代理模式的使用(其实我就是因为没看懂,才学的),文章...

  • Retrofit源码分析总结

    Retrofit怎么进行网络请求 Retrofit主要是在create方法中采用动态代理模式实现接口方法,这个过程...

  • Retrofit的设计模式 -- 动态代理模式

    Retrofit中的动态代理模式 最近有时间开始了看源码的节奏,看了Retrofit源码上来就看到了动态代理模式,...

  • Retrofit的设计模式 -- 享元模式

    Retrofit中的享元模式 上次看到Retrofit中的动态代理模式,其中有段代码如下: 其中用到的享元模式就是...

  • 设计模式汇总(ongoing)

    单例模式 工厂方法模式 抽象工厂模式 模板方法模式 建造者模式 代理模式Binder 通信retrofit 使用动...

  • Retrofit2.0- 源码分析

    1. 阅读引导 在分析 Retrofit 源码之前,你首先得理解动态代理模式,因为Retrofit是通过动态代理的...

  • Java中的代理模式

    因为Retrofit剖析源码的时候会用到ava中的代理模式,所以这篇就先回忆一下代理设计模式。代理模式分为两种:代...

  • Retrofit

    Retrofit设计模式 动态代理,装饰模式,建造者模式,抽象工厂模式,适配器模式 建造者模式创建Retrofit...

网友评论

      本文标题:代理模式,及在Retrofit中的使用

      本文链接:https://www.haomeiwen.com/subject/hrxhphtx.html