代理模式以及在Android中的使用

本文涉及java中的一些反射知识,若是你对反射这部分不太熟悉,建议先去了解一下反射知识,或者看一下个人这篇文章 Java反射以及在Android中的使用javascript

代理模式

1、定义

某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。php

举例说明:css

代理模式从字面上就是咱们理解的生活中那个中介代理,好比公司A(原对象)为海外公司,消费者B(某一个对象)直接从公司A购买商品须要各类各样复杂的步骤,这时候就出现了代理人C(代理对象),让他来替咱们去处理那些复杂的步骤,咱们只须要告诉代理人C咱们须要什么商品就能够了,由代理人C去跟公司A进行购买,消费只须要等着收快递,其余的不用关心。java

2、使用代理好处

一、经过引入代理对象的方式 来间接访问目标对象,防止直接访问目标对象给系统带来的没必要要复杂性; python

二、经过代理对象对原有的业务加强;android

3、UML图

公共接口角色:定义了委托角色代理角色的共同接口或者抽象类。git

委托角色(公司A) :实现或者继承抽象主题角色,定义实现具体业务逻辑的实现。github

代理角色(代理C) : 实现或者继承抽象主题角色,持有委托角色的引用,控制和限制委托角色的实现,而且拥有本身的处理方法(预处理和蔼后)。web

特色:数组

  • 委托角色代理角色共同继承同一个接口或者实现抽象类

  • 代理角色持有委托角色对象的引用

4、静态代理

根据上面的例子,咱们来实现一个静态代理:

静态代理在使用时,须要先定义接口或者抽象类,委托类代理类一块儿实现相同的接口或者是继承相同抽象类。通常来讲,委托类代理类是一对一的关系,固然一个代理对象对应多个委托类也是能够的。

步骤 :定义公共接口——>建立委托类(公司A)——>建立代理类(代理C)——>访问(消费者执行)

一、定义公共接口

/**
 * @author : EvanZch
 * description: 抽象主题角色,商家和代理销售这个操做
 **/

public interface IFactoryA {
    void saleManTools(String size);
}
复制代码

二、委托类 (公司A)

/**
 * @author : EvanZch
 *         description: 公司A, 实现IFactory
 **/

public class FactoryA implements IFactoryA {
    /**
     * 实现具体的业务逻辑
     *
     * @param size
     */

    @Override
    public void saleManTools(String size) {
        System.out.println("公司A——>出货:" + size + "MM");
    }
}
复制代码

三、代理类 (代理C)

/**
 * @author : EvanZch
 * description: 代理C,实现IFactory,并持有真实对象FactoryA引用
 **/

public class ProxyC implements IFactoryA {

    private IFactoryA factory;
    /**
     * 持有被代理角色的引用
     * @param iFactory
     */

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

    /**
     * 对真实对象方法进行加强
     * @param size
     */

    @Override
    public void saleManTools(String size) {
        doBefore();
        factory.saleManTools(size);
        doAfter();
    }

    private void doBefore() {
        System.out.println("代理C——>根据客户需求定制方案");
    }

    private void doAfter() {
        System.out.println("代理C——>收集使用反馈");
    }
}
复制代码

在执行被代理类方法前,能够进行功能拓展,符合开闭原则。

四、消费者

/**
 * @author : EvanZch
 * description: 消费者B
 **/

public class ConsumerB {
    public static void main(String[] args) {
        IFactoryA factoryA = new FactoryA();
        ProxyC proxyC = new ProxyC(factoryA);
        proxyC.saleManTools("36D");
    }
}
复制代码

结果:

若是这个时候出现了专卖女性用品的公司B,咱们须要按照下面步骤再走一遍

定义公共接口——>建立委托类——>建立代理类——>访问

优势:

静态代理好处就是能对目标对象方法进行功能拓展。

上面例子中能够看到,海外公司只负责发货,代理类能够在不改动委托类的状况下对目标人群进行需求方案定制和使用状况反馈收集工做,对委托类的方法进行了功能拓展。

缺点:

静态代理,一对一(一个代理只代理一个公司)则会出现代理对象量多、代码量大,从而致使代码复杂,可维护性差的问题,一对多(一个代理代理多个公司)则代理对象会出现扩展能力差的问题。

能够看到咱们公共接口是销售男性用品,若是后续需求增长女性用品是否是又要改动接口或者增长接口?接口一改动,代理类也要跟着改,牵一发而动全身,当需求愈来愈多愈来愈复杂的时候,就会使整个代码臃肿,而且维护性变差。

5、动态代理

使用动态代理没必要要本身在去实现代理类,只须要一个动态代理类 (代理公司) 就可让程序运行在期间动态的建立接口的实现。

动态代理实现的关键是须要使用 InvocationHandler接口和经过 Proxy 类动态建立对象。

步骤 :定义公共接口——>建立委托类(公司A)——>只须要建立一个动态代理类(代理公司)——>访问(消费者执行)

一、InvocationHandler 接口

处理动态代理类对象方法的调用,每一个动态代理类都会关联一个。

package java.lang.reflect;

/**
 * {@code InvocationHandler} is the interface implemented by
 * the <i>invocation handler</i> of a proxy instance.
 *
 * <p>Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method
 * invocation is encoded and dispatched to the {@code invoke}
 * method of its invocation handler.
 *
 * @author      Peter Jones
 * @see         Proxy
 * @since       1.3
 */

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

二、Proxy类

用来建立代理对象的类,是全部动态代理类的父类,主要使用 newProxyInstance 方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler h)

复制代码

先贴动态代理类实现的代码:

/**
 * @author : EvanZch
 *         description: 动态代理类,必须实现InvocationHandler接口
 **/

public class ProxyCompany implements InvocationHandler {
    /**
     * 依旧持有真实对象
     */

    private Object mFactory;

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

    /**
     * 获取动态代理对象
     */

    public Object getDynamicProxy() {
        /**
         * 拿到动态代理对象
         * ClassLoader loader :真实对象的ClassLoader
         * Class<?>[] interfaces : 真实对象实现的接口
         * InvocationHandler h  : InvocationHandler对象
         */

        return Proxy.newProxyInstance(mFactory.getClass().getClassLoader()
                , mFactory.getClass().getInterfaces(), this);
    }

    /**
     * InvocationHandler 接口方法
     *
     * @param proxy  代理类自己
     * @param method 咱们所要调用某个对象真实的方法的 Method 对象
     * @param args   method 对象中自己须要传入的参数
     * @return
     * @throws Throwable
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        // 调用真实对象方法
        Object result = method.invoke(mFactory, args);
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("代理公司——>方案制定");
    }

    private void doAfter() {
        System.out.println("代理公司——>收集反馈");
    }
}
复制代码

调用动态代理

        // 建立动态代理对象
        ProxyCompany proxyCompany = new ProxyCompany();
        // 公司A
        IFactoryA factoryA = new FactoryA();
        // 动态代理引入真实对象
        proxyCompany.setFactory(factoryA);
        // 动态的建立代理类
        IFactoryA proxyA = (IFactoryA) proxyCompany.getDynamicProxy();
        proxyA.saleManTools("F");
复制代码

结果:

仍然获取正确结果,纵观整个过程,咱们并无实质的建立一个代理类,整个过程只须要一个动态代理类就能完成,若是这个时候咱们出现了公司B,咱们只须要执行的步骤 定义公共接口——>建立委托类(公司B)——>访问(消费者执行) 具体代码就不贴了,跟公司A相似,我把调用代码贴在一块儿,你品,细细的品。

        // 建立动态代理对象
        ProxyCompany proxyCompany = new ProxyCompany();
        // 公司A   IFactoryA:公共接口   FactoryA:委托类(公司A)
        IFactoryA factoryA = new FactoryA();
        // 动态代理引入真实对象
        proxyCompany.setFactory(factoryA);
        // 动态的建立代理类
        IFactoryA proxyA = (IFactoryA) proxyCompany.getDynamicProxy();
        proxyA.saleManTools("F");

        // 公司B   IFactoryB:公共接口   FactoryB : 委托类(公司B)
        IFactoryB factoryB = new FactoryB();
        proxyCompany.setFactory(factoryB);
        IFactoryB proxyB = (IFactoryB) proxyCompany.getDynamicProxy();
        proxyB.saleWomanTool(180);
复制代码

结果:

不知道各位看官可有一点感悟,咱们能够看到,就算这个时候出现了公司B,咱们整个过程也没有建立真实的代理对象,而是直接经过一个动态代理类中 proxyCompany.getDynamicProxy() 来动态的获取咱们的代理类对象。既然是动态代理,那代理类确定存在,只是jdk动态的给咱们生成,那真实的代理类是谁?怎么建立的?

咱们先来看代理类是谁这个问题,咱们对刚刚的代码进行debug调试

从日志输入信息里面能够看到,jdk为在运行时分别给咱们的 IFactoryAIFactoryB 生成了名字为$Proxy0$Proxy1 的代理类,那它在哪产生的?带着这个问题,咱们开始深刻。

6、源码分析

咱们是经过 这个方法来获取动态代理对象,那咱们从这里切入,先看看newProxyInstance作了啥?

若是你对反射熟悉的话,图片中标注的几处你应该很容易知道在干吗。

A:获取类的Class对象

B:反射获取其构造方法

C:遍历构造赋予权限

D:返回该类的实例

这里出现的类,没的说,确定就是咱们须要的那个实际的代理类,咱们再看一下 getProxyClass0 方法作了啥?

感受贴图比贴代码舒服,就直接贴图了,这个方法很简单,先对接口数目进行判断,65535这个数字搞Android的就很熟悉了吧,方法数不能超过65535,这不是咱们本文讨论的关键,关键看 proxyClassCache.get(loader, interfaces)

咱们看到这个方法传入了咱们设置的 ClassLoader参数 和 interfaces 参数 进入 get 方法里面

咱们看到标识这里,传入了咱们的参数,应该是一行关键性代码,咱们再看它作了啥?

能够看到它是一个接口方法,这个时候咱们须要再去找方法的实现类

能够看到 apply 方法实现类有 KeyFactoryProxyClassFactory 聪明你的看名字也应该知道,咱们只须要关注 ProxyClassFactory 这个类

image
image

这里隐隐约约出现了 proxyName, 还记得咱们前面debug调试出现的 $Proxy0$Proxy1吗?

private static final String proxyClassNamePrefix = "$Proxy";
String proxyName = proxyPkg + proxyClassNamePrefix + num;
复制代码

看到 $Proxy 了吗? 原来名字就是从这里出来的

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
复制代码

这里经过ProxyGenerator类生成指定的代理类字节数组。

其实jdk生成.class文件的内容就是一串串字节数组

defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
// 这是一个 native 方法
private static native Class<?> defineClass0(ClassLoader loader, String name,
 byte[] b, int off, int len);
复制代码

再经过 defineClass0方法 经过指定ClassLoader生成代理类的Class对象,到这里,咱们前面关于动态产生的代理类怎么产生的问题也就解决了。

既然 ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags) 方法生成指定代理类的字节数组,那咱们能够经过这个方法来看看,具体内容是啥?

咱们经过 generateProxyClass 获取到字节数组,并保存到本地。

    public static void generateProxyClass(String proxyName, Class clazz{
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces(), 1);
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
复制代码

将咱们动态产生的类,用上面的方法保存到本地。

generateProxyClass(proxyA.getClass().getSimpleName(), factoryA.getClass());
generateProxyClass(proxyB.getClass().getSimpleName(), factoryB.getClass());
复制代码

能够看到生成的两个class文件,名字恰好跟咱们前面debug看到的一直,咱们反编译文件看一下里面是啥。

idea 打开.class文件自动进行反编译。

$Proxy0

public class $Proxy0 extends Proxy implements IFactoryA {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    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 void saleManTools(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } 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 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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.evan.proxy.staticProxy.IFactoryA").getMethod("saleManTools"new Class[]{Class.forName("java.lang.String")});
            m2 = Class.forName("java.lang.Object").getMethod("toString"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());
        }
    }
}
复制代码

关键信息:

一、 $Proxy0 继承至 Proxy

二、实现了 IFactoryA 接口

三、实现了咱们接口里面的方法 :saleManTools(String var1)

能够看到咱们动态产生的代理类 $Proxy0 继承至 Proxy ,前面也说过,Proxy 是全部动态代理类的父类,全部动态代理类都须要继承它,经过生成的文件,咱们能够证明这点。

前面提到代理模式特色一直就是 代理类和委托类要同时实现一个接口或者实现抽象类,这里能够看到,咱们建立的动态代理类一样也实现了咱们的 IFactoryA 接口。

咱们再看一下saleManTools方法的实现

    public final void saleManTools(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
复制代码

关键信息:

super.h.invoke(this, m3, new Object[]{var1});

这个 h 是啥?在动态产生的代理类 $Proxy0 没看到这个参数,咱们再在其父类 Proxy 中查看

image
image

就是咱们前面动态代理类中实现的 InvocationHandler 接口。因此在 saleManTools 方法中,再调用了 InvocationHandler 接口的 invoke 方法,咱们再回忆一下前面写动态代理类时候,怎么处理invoke方法的?回忆不起来,我就再贴一次!

    /**
     * InvocationHandler 接口方法
     *
     * @param proxy  代理类自己
     * @param method 咱们所要调用某个对象真实的方法的 Method 对象
     * @param args   method 对象中自己须要传入的参数
     * @return
     * @throws Throwable
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        // 调用真实对象方法
        Object result = method.invoke(mFactory, args);
        doAfter();
        return result;
    }
复制代码

再看 saleManTools 方法实现中的这行,你对比着看,你品,细细的品。

super.h.invoke(this, m3, new Object[]{var1});
复制代码

那这里的m3不就是Method嘛

m3 = Class.forName("com.evan.proxy.staticProxy.IFactoryA").getMethod("saleManTools"new Class[]{Class.forName("java.lang.String")});
复制代码

再看m3的值,不就是经过反射拿到 咱们本身定义的IFactoryA 接口的 saleManTools 方法???看到这里,再回头去看看咱们前面的动态代理类,你对 InvocationHandlerProxy 这两个关键类应该就有了更清晰的认识了,若是没有,就再看一遍??

好了,以上源码分析内容基本就是jdk对静态代理的实现(这个车是否是刹的有点快,哈哈)。

7、动态代理在Android中的运用

retrofit 这个网络请求库我相信搞Android的大哥们应该都用过吧,咱们通常怎么操做?

一、编写 xxxApi 接口

public interface xxxApi {
     String HOST = "xxxxxxxxxx";
     @POST("app/xxxx")
     @FormUrlEncoded
     Observable<BaseResponse<String>> sendEmailCode(@Field("email") String email);
}
复制代码

二、初始化 retrofit :

Retrofit retrofit = new Retrofit.Builder()
 .baseUrl("https://xxxxxx")
 .build();
复制代码

三、动态建立 xxxApi 实例

xxxApi service = retrofit.create(xxxApi.class);
service.sendEmailCode(xxxx);
复制代码

有没有很熟悉,咱们 create 的时候不是只传了一个 interface 进去吗?怎么就能够直接经过返回的实例调用方法了呢?跟咱们前面的动态代理是否是有几分类似?咱们去看看Retrofit的源码,看他 Create 到底操做了啥?

咱们在github上能够看到 retrofit 的 create 方法

image
image

看到 Proxy.newProxyInstance 这个方法,就应该很清楚了,证明咱们前面的猜想,至于具体怎么操做的跟前面的相似,这里就再也不分析了。

相关文章
相关标签/搜索