代码段学习设计模式 -- 代理模式

作者: GYLEE | 来源:发表于2018-06-11 11:40 被阅读32次

    也被称为委托模式,代理模式为其他对象提供一种代理以控制对这个对象的访问。

    目录


    [TOC]

    1、组成及优点

    1.1 组成

    • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
    • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
    • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

    1.2 优点

    • 职责清晰:真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事务,通过后期代理完成非本职工作。
    • 高扩展性:具体主题角色可以随时发生变化,只要它实现了接口,而不用关心它如何变化,只要接口没变,代理类可以在完全不做修改的情况下使用。
    • 智能化:通过动态代理的方式实现在编码阶段不需要知道代理的对象。
    • 扩展原功能,不侵入原代码。
    • 普通代理模式,在该模式下调用者只知道代理而不用知道真实角色,屏蔽了真实角色的变更对高层模块的影响,在实际项目中,一般都是通过约定来禁止new一个真实的角色。

    1.3 缺点

    • 代理类必须实现接口,导致代理类太多;
    • 一旦接口发生变更,代理类也必须同步更新,导致维护成本会比较高;

    2、静态代理

    1.1 情景模拟

    秉承着通过代码段学习设计模式的的宗旨,我们通过生活中一个常见的情景作为一个栗子:小明同学通过代码的方式从国外购买一台 Mac
    以下为根据此情况设计的 UML 图:

    UML图

    1.2 具体代码

    1.2.1 定义的接口

    /**
     * Time: 2018/6/6  17:45
     * 定义目标对象的接口方法
     * 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
     */
    public interface Subject {
        public  void buybuybuy();
    }
    
    

    1.2.2 目标类(被代理类)

    /**
     * Time: 2018/6/6  17:45
     * 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
     * 实现抽象目标对象的接口
     * 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
     */
    
    public class XiaoMing implements Subject {
        private static final String TAG = "_XiaoMing";
    
        @Override
        public void buybuybuy() {
            Log.e(TAG, "buybuybuy: 小明要买Mac");
        }
    }
    

    1.2.3 代理类

    /**                                                           
     * Time: 2018/6/7 0007 10:58                                  
     * 真正进行操作的对象 代理对象   
     * 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法* 来实现抽象方法,并可以附加自己的操作。                            */                                                           
    public class ForginerBuyer implements Subject {               
        private static final String TAG = "_ForginerBuyer";       
        private Subject mSubject;                                 
                                                                  
        public ForginerBuyer(Subject mSubject) {                  
            this.mSubject = mSubject;                             
        }                                                         
                                                                  
        @Override                                                 
        public void buybuybuy() {                                 
            Log.e(TAG, "buybuybuy: 代购开始购买");                      
            mSubject.buybuybuy();                                 
            Log.e(TAG, "buybuybuy: 代购结束购买");                      
        }                                                         
    }                                                             
    
    

    1.2.4 客户端

    public class BuyActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_buy);
            //创建 XiaoMing 实例对象
            Subject mXiaoMing = new XiaoMing();
            //创建 ForginerBuyer 代购对象,并把 XiaoMing 实例对象传入
            Subject mBuyer = new ForginerBuyer(mXiaoMing);
            mBuyer.buybuybuy();
           
        }
    }
    
    

    1.2.5 执行结果

    E/_ForginerBuyer: buybuybuy: 代购开始购买
    E/_XiaoMing: buybuybuy: 小明要买Mac
    E/_ForginerBuyer: buybuybuy: 代购结束购买
    

    1.2.6 小结

        通过以上代码,稍微思考一下其实在平时静态代理十分的常见,就是把真正的对象包装一下成为一个新的类,通过访问这个新的类我们就可以真正的对目标类对象进行操作。但是我们需要让代理类和目标类发生关系 -- 把目标类对象传入代理类中。
        但是难道我们在使用代理模式时必须要首先构建一个抽像对象吗?我个人觉得不是必须的。即使代理类和目标类没有共同的方法,只要把目标类的实例对象传入代理类中,在代理类中调用目标类的相应方法,那么也能够完成代理的最终目的。

    3、动态代理

    动态代理和静态代理的不同:在程序运行前,代理类不存在,而是在程序运行时通过反射机制生成。
    由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。动态代理类:在程序运行时,运用反射机制动态创建而成。

    还是通过上面 小明海外代购一台 Mac 的栗子来说明 动态代理的具体实现。

    3.1 具体代码

    3.1.1 定义接口

    /**
     * Time: 2018/6/6  17:45
     * 定义目标对象的接口方法
     * 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
     */
    public interface Subject {
        public  void buybuybuy();
    }
    

    3.1.2 目标类

    /**
     * Time: 2018/6/6  17:45
     * 小明,真正的想买Mac的对象 = 目标对象 = 被代理的对象 = 委托类
     * 实现抽象目标对象的接口
     * 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调* 用。
     */
    
    public class XiaoMing implements Subject {
        private static final String TAG = "_XiaoMing";
    
        @Override
        public void buybuybuy() {
            Log.e(TAG, "buybuybuy: 小明要买Mac");
        }
    }
    
    

    3.1.3 声明调用处理器类

    /** 作用
     * 1.  生成 动态代理对象
     * 2.  指定 代理对象运行目标对象方法时需要完成的 具体任务
     */
    public class DynamicProxy implements InvocationHandler {
        private static final String TAG = "DynamicProxy";
        private Object ProxyObject;
    
        public Object newProxyInstance(Object ProxyObject){
            this.ProxyObject =ProxyObject;
            return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                    ProxyObject.getClass().getInterfaces(),this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Log.e(TAG, "invoke: 代购出门了" );
            Object result = null;
            // 通过Java反射机制调用目标对象方法
            result = method.invoke(ProxyObject, args);
            return result;
        }
    }
    

    3.1.4 通过动态代理对象,调用目标对象的方法

    
    public class Main2Activity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            // 1. 创建调用处理器类对象
            DynamicProxy DynamicProxy = new DynamicProxy();
            // 2. 创建目标对象对象
            XiaoMing mXiaoMing = new XiaoMing();
            // 3. 创建动态代理类 & 对象:通过调用处理器类对 newProxyInstance()
            // 传入上述目标对象对象
            Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
            // 4. 通过调用动态代理对象方法从而调用目标对象方法
            // 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
            mSubject.buybuybuy();
        }
    }
    

    注:以上代码参考 Carson_Ho的简书

    3.1.5 执行结果

    E/_DynamicProxy: invoke: 代购出门了
    E/_XiaoMing: buybuybuy: 小明要买Mac
    

    3.2 代码解析

    在静态代理模式中,代理类是通过我们我们自己编辑的,根据动态代理模式的特点:在程序运行期生成代理类,那么动态代理类是怎么生成的呢?怎么通过调用动态代理类的实例来调用目标类的方法?下面我们通过具体代码来说明这两个问题

    3.2.1 生成代理类

    很明显,在具体的调用中我们是通过以下代码生成代理类的:

    Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
    

    跳转到具体代码(终点关注标注(重要代码)的代码段)

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            // Android-changed: sm is always null
            // final SecurityManager sm = System.getSecurityManager();
            // if (sm != null) {
            //     checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            // }
    
            /*
             * (重要代码)
             * Look up or generate the designated proxy class.
             * 查找或者生成代理类,正是在此处我们通过代码生成了代理类,
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                // }
                //(重要代码)
                //通过反射获得动态代理的构造器对象
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    // Android-changed: Removed AccessController.doPrivileged
                    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);
            }
        }
    

    但是具体是怎么生成动态代理类的呢,我们具体参考Class<?> cl = getProxyClass0(loader, intfs);

    private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            return proxyClassCache.get(loader, interfaces);
        }
    

    proxyClassCacheproxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());我们需要关注的是 ProxyClassFactory

    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
              ....
                    Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                    Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
    
                    /*
                     * Choose a name for the proxy class to generate.
                     */
                    long num = nextUniqueNumber.getAndIncrement();
                    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                    return generateProxy(proxyName, interfaces, loader, methodsArray,
                                         exceptionsArray);
                }
            }
    
    

    巴拉巴拉一大堆,我不想看这是什么鬼,反正最后来到了了
    generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray);,那我们来看一个generateProxy(...)

    @FastNative
        private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                     ClassLoader loader, Method[] methods,
                                                     Class<?>[][] exceptions);
    

    这是一个 Native方法,具体为生成代理类,我在网上查看一下资料时看到有些博客上可以调用相应的 api 生成具体的可以看到的代理类,但是我在具体的 demo 中不能进行此操作,故此代码跟踪完毕。

    3.3 与静态代理相比的优点

    当委托类特别多时,即在每个委托类都对应着一套规则(即每一个委托类都对应着一个抽象对象),那么相应的如果使用静态代理模式,那么就需要实现多个相应的代理类,这样代理类个数会比较多。但是如果使用动态代理模式,此时在生成 InvocationHandler相应的实例时传入不同的委托类实例对象,那么则可以实现同事代理多个委托类的功能。

    DynamicProxy DynamicProxy = new DynamicProxy();
    XiaoMing mXiaoMing = new XiaoMing();
    Subject mSubject = (Subject) DynamicProxy.newProxyInstance(mXiaoMing);
    mSubject.buybuybuy();
    XiaoHong mXiaoHong= new XiaoHong();
    Subject2 mSubject2 = (Subject2) DynamicProxy.newProxyInstance(mXiaoHong);
    mSubject2 .buybuybuy2();
    
    

    即在委托类比较多时,建议使用动态代理模式


    优秀博客:
    代理模式
    Java 动态代理
    动态代理相对于静态代理的优势
    代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

    相关文章

      网友评论

        本文标题:代码段学习设计模式 -- 代理模式

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