美文网首页
代理设计模式-从Retrofit的create方法分析动态代理

代理设计模式-从Retrofit的create方法分析动态代理

作者: _风听雨声 | 来源:发表于2020-04-14 14:44 被阅读0次

    代理在生活中非常常见,如我们平时买东西,我们不会去工厂买,而是去超市或者网购。超市和网购平台实际上就是一种代理,他们代理了我们去向工厂或者向中间商购买和运输的过程。这是生活中的代理,那么我们开发中的代理模式是怎样的呢?

    代理模式

    定义
    为其他对象提供一种代理,以控制这个对象的访问。

    涉及角色及说明
    Subject(抽象主题类):接口或抽象类,声明真实主题与代理的共同接口方法。
    RealSubject(真实主题类):也被叫做代理类或被委托类,定义了代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接的调用真实主题类的方法。
    Proxy(代理类):也叫委托类,持有对真实主题的引用,在其所实现的接口中调用真实主题类中相应的接口方法执行。
    Client(客户端类):使用代理模式的地方

    以日常购买东西为例子,消费者只需要关心购买东西(静态代理)

    抽象主题类
    定义了真实主题与代理的共同接口方法

    public interface IBuy {
        void buy();
    }
    

    真实主题类
    消费者只关心买东西

    public class Consumer implements IBuy {
        @Override
        public void buy() {
            System.out.println("消费者买东西");
        }
    }
    

    代理类
    持有真实主题类的引用,为真实主题类提供额外服务

    public class Supermarket implements IBuy {
        private IBuy consumer;
    
        public Supermarket(IBuy consumer) {
            this.consumer = consumer;
        }
    
        @Override
        public void buy() {
            System.out.println("从工厂买回来");
            consumer.buy();
            System.out.println("提供售后服务");
        }
    }
    

    客户端

    public class Client {
    
        public static void main(String[] args) {
            Consumer consumer = new Consumer();
            Supermarket supermarket = new Supermarket(consumer);
            supermarket.buy();
        }
    }
    

    输出结果:

    从工厂买回来
    消费者买东西
    提供售后服务
    

    小结一下
    静态代理的代理类在编译时期已经存在,一般需要先定义接口,被代理对象和代理对象共同实现这个接口
    优点:
    1.职责清晰,被代理角色只实现实际的业务逻辑,代理对象实现附加的处理逻辑
    2.扩展性高,可以更换不同的代理类,实现不同的代理逻辑
    缺点也比较明显
    如果代理的业务类型增加,需要新增接口方法,同时代理类和被代理类都需要对应的去新增方法。如果新增的业务量非常多,那么代理类的代码将会暴增,可读性降低难以维护。

    同样以日常购买东西为例子(动态代理

    代理类

    public class BuyInvocationHandler implements InvocationHandler {
        private IBuy mConsumer;
    
        public BuyInvocationHandler(IBuy mConsumer) {
            this.mConsumer = mConsumer;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("从工厂买回来");
            Object returnObject = method.invoke(mConsumer, args);
            System.out.println("提供售后服务");
            return returnObject;
        }
    }
    

    真实主题类

    public class Consumer implements IBuy {
        @Override
        public void buy() {
            System.out.println("消费者买东西");
        }
    }
    

    客户端

    public class Client {
    
        public static void main(String[] args) {
            Consumer consumer = new Consumer();
            IBuy consumerBuy = (IBuy) Proxy.newProxyInstance(consumer.getClass().getClassLoader(), consumer.getClass().getInterfaces(),
                    new BuyInvocationHandler(consumer));
            consumerBuy.buy();
        }
    }
    

    输出结果:

    从工厂买回来
    消费者买东西
    提供售后服务
    

    小结一下
    动态代理是指运行时动态生成一个代理对象,相对于编译时的静态来区分。动态代理可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为。
    这个动态生成的代理类帮我们做一些逻辑处理,主要是利用字节码技术创建Proxy类然后通过反射创建实例。

    InvocationHandler是动态代理接口,动态代理类需要实现该接口,并在invoke方法中对代理类的方法进行处理

    public interface InvocationHandler {
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable;
    }
    

    参数说明
    *Object proxy:被代理的对象
    *Method method:要调用的方法
    *Object[] args:方法调用所需要的参数

    Proxy类可以通过newProxyInstance创建一个代理对象

    @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
            Class<?> cl = getProxyClass0(loader, intfs);
    
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
    
                    cons.setAccessible(true);
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    

    参数说明:
    *ClassLoader loader:类加载器
    *Class<?>[] interfaces:要代理的接口
    *InvocationHandler h:实现InvocationHandler接口的子类

    生成的代理类大概如下:

    public final class $Proxy0 extends Proxy implements IBuy { 
      //这4个Method对象分别代表equals()、toString()、buy()、hashCode()方法 
      private static Method m1; 
      private static Method m2; 
      private static Method m3; 
      private static Method m0; 
      //构造方法接收一个InvocationHandler对象为参数,这个对象就是代理类的“直接委托类”(真正的委托类可以看做代理类的“间接委托类”)
      public $Proxy0(InvocationHandler var1) throws { 
        super(var1); 
      } 
      //对equals方法的调用实际上转为对super.h.invoke方法的调用,父类中的h即为我们在构造方法中传入的InvocationHandler对象,以下的toString()、buy()、hashCode()等方法同理 
      public final boolean equals(Object var1) throws { 
        try { 
          return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); 
        } catch (RuntimeException | Error var3) { 
          throw var3; 
        } catch (Throwable var4) { 
          throw new UndeclaredThrowableException(var4); 
        } 
      } 
      public final String toString() throws { 
        try { 
          return (String)super.h.invoke(this, m2, (Object[])null); 
        } catch (RuntimeException | Error var2) { 
          throw var2; 
        } catch (Throwable var3) { 
          throw new UndeclaredThrowableException(var3); 
        } 
      } 
      public final void buy() throws { 
        try { 
          super.h.invoke(this, m3, (Object[])null); 
        } catch (RuntimeException | Error var2) { 
          throw var2; 
        } catch (Throwable var3) { 
          throw new UndeclaredThrowableException(var3); 
        } 
      } 
      public final int hashCode() throws { 
        try { 
          return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); 
        } catch (RuntimeException | Error var2) { 
          throw var2; 
        } catch (Throwable var3) { 
          throw new UndeclaredThrowableException(var3); 
        } 
      } 
      //这里完成Method对象的初始化(通过反射在运行时获得Method对象) 
      static { 
        try { 
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); 
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 
          m3 = Class.forName("Sell").getMethod("sell", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 
        } catch (NoSuchMethodException var2) { 
          throw new NoSuchMethodError(var2.getMessage()); 
        } catch (ClassNotFoundException var3) { 
          throw new NoClassDefFoundError(var3.getMessage()); 
        } 
      }
    }
    

    Retrofit的代理模式
    基本使用:
    (1).定义接口

    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    }
    

    (2).新建retrofit对象,然后产生一个接口对象,然后调用具体方法去完成请求。

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    

    (3).由创建的GitHubService的向web服务器发出同步或异步HTTP请求

    Call<List<Repo>> repos = service.listRepos("octocat");
    

    retrofit.create方法就是通过动态代理的方式传入一个接口,返回了一个对象

    public <T> T create(final Class<T> service) {
        validateServiceInterface(service);//1
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              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);//2
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);//3
                }
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);//4
              }
            });
    }
    

    注释分析
    *1.判断是否是一个接口
    *2.判断调用的方法是否属于Object,如toString,hashCode,属于Object执行
    *3.判断平台是java还是Android,Android不会进
    *4.解析接口的方法、注解、参数并执行方法(这里是Retrofit重点方法,代码细节超出本文主题范围,将在Retrofit源码分析重点探讨)

    Retrofit为什么要这样设计?
    个人认为主要有两点
    *1.应用的请求接口可能有很多个,通过代理模式,能动态的为每个接口生成具体的代理类,并实现了我们的接口,我们不需要关心具体的细节,我们只需要声明接口然后传递给Retrofit即可,然后有Retrofit动态生成具体请求对象,发起请求并把结果返回给我们。
    *2.让开发者与Retrofit的底层实现okhttp完全解耦,哪怕作者后期决定底层实现不再采用okhttp,开发者的代码依然不需要改变

    相关文章

      网友评论

          本文标题:代理设计模式-从Retrofit的create方法分析动态代理

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