美文网首页
android常用设计模式之代理设计模式及动态代理原理

android常用设计模式之代理设计模式及动态代理原理

作者: 好大一只龍 | 来源:发表于2018-01-24 21:10 被阅读30次

    定义:代理模式属结构型设计模式。为其他对象提供一种代理以控制对这个对象的访问。

    代理模式结构图

    代理类结构图.jpg

    在代理模式中有如下角色:

    • ISubject: 抽象主题类,声明真实主题与代理的共同接口方法。
    • RealSubject:真实主题类,代理类所代表的真实主题。客户端通过代理类间接地调用真实主题类的方法。
    • Proxy:代理类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。

    1. 简单实现代码

    public interface IShop {
        void buy();
    }
    
    public class BuyProxy implements IShop {
    
        private IShop mShop;
    
        public BuyProxy(IShop shop){
            mShop = shop;
        }
    
        @Override
        public void buy() {
            mShop.buy();
        }
    }
    
    public class Customer implements IShop {
        @Override
        public void buy() {
            System.out.print("顾客购物");
        }
    }
    
    public class BuyProxyTest {
        private Customer mCustomer;
        private BuyProxy mBuyProxy;
        @Before
        public void setUp() throws Exception {
            mCustomer = new Customer();
            mBuyProxy = new BuyProxy(mCustomer);
        }
    
        @Test
        public void buy() throws Exception {
            mBuyProxy.buy();
        }
    
    }
    

    2.动态代理

    从编码角度来说,代理模式分为静态代理和动态代理。上面的例子是静态代理,在代码运行前就已经存在了代理类的class编译文件;而动态代理则是在代码运行时通过反射来动态生成类并确定代理谁。Java提供动态代理接口InvocationHandler,实现该接口需要重写invoke方法。
    下面用动态代理实现上面的例子,代码如下:

    //动态代理类
    public class DynamicBuyProxy implements InvocationHandler {
        IShop shop;
    
        public DynamicBuyProxy setShop(IShop shop) {
            this.shop = shop;
            return this;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object resutl = method.invoke(shop, args);
            if("buy".equals(method.getName())){
                System.out.println("---通过动态代理购买---");
            }
            return null;
        }
    }
    
    //动态代理模式单元测试类
    public class DynamicBuyProxyTest {
        DynamicBuyProxy mDynamicProxy;
        IShop mCustomer;
        ClassLoader mClassLoader;
        @Before
        public void setUp() throws Exception {
            mCustomer = new Customer();
            mDynamicProxy = new DynamicBuyProxy();
            mDynamicProxy.setShop(mCustomer);
            mClassLoader = mCustomer.getClass().getClassLoader();
        }
    
        @Test
        public void invoke() throws Exception {
            IShop proxyer = (IShop) Proxy.newProxyInstance(mClassLoader, new Class[]{IShop.class}, mDynamicProxy);
            proxyer.buy();
            System.out.println("proxyer.classname:"+proxyer.getClass().getName()); //Attend01
        }
    }
    

    注意上面代码注释Attend01处, 我们输出了动态代理为我们生成的代理类对象类型。
    执行单元测试后结果如下:


    dynamicproxy.png

    意料之中的是代理类正常的输出了我们想要的代理类逻辑。
    而代理类类型却出乎我们意料com.sun.proxy.$Proxy5,从这里引出它的原理。

    原理

    实际上通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
    下面来看它的源码:

    import java.lang.reflect.InvocationHandler;   
    import java.lang.reflect.Method;  
    import java.lang.reflect.Proxy;   
    import java.lang.reflect.UndeclaredThrowableException;  
       
    public final class $Proxy0 extends Proxy implements IShop {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
                m3 = Class.forName("com.zyl.designpatterns.structuralpatterns.proxy.IShop").getMethod("buy",  
                        new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                        .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public void buy() {  
            try {  
                super.h.invoke(this, m3, null);  //Attend02
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
      
        }  
    }  
    

    可以看到上面代码注释Attend02中h实际上就是我们的InvocationHandler接口的实现类DynamicBuyProxy,调用它的invoke方法就是调用了我们InvocationHandler.invoke()方法。
    是不是豁然开朗了,实际上它就是JVM为我们生成了一个代理类,静态代理是我们编译之前写好的, 而动态代理是由JVM根据我们提供的接口为我们动态生成的。

    场景

    是不是感觉用它的地方不多呢,但是实际上动态代理场景有很多,比如Spring的核心AOP、Android最近大火的Retrofit等等。

    优点

    • 真实主题类就是实现实际的业务逻辑,不用关心其他的非本职工作。
    • 任何主题类随时都会该发生变化,但是因为它实现了公共接口,所以代理类可以不做任何修改就能够使用。

    如果对动态代理的作用还是比较模糊, 建议看看这篇知乎的解答Java 动态代理作用是什么?

    代码已上传github

    相关文章

      网友评论

          本文标题:android常用设计模式之代理设计模式及动态代理原理

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