美文网首页
代理模式

代理模式

作者: code希必地 | 来源:发表于2020-09-16 17:13 被阅读0次

    1、什么是代理模式

    定义:给目标对象提供一个代理对象,并由代理对象控制目标对象的引用。
    在代理模式中需要代理对象和目标对象实现同一个接口,看下UML图

    image.png

    2、为什么要用代理

    代理的作用:使用代理模式,可以在不修改目标对象方法的前提下,对方法进行包装增强。
    举个例子

    public interface IPerson{
            void say();
        }
        public class Student implements IPerson{
    
            @Override
            public void say() {
                Log.e("TAG","Student Say");
            }
        }
    

    Student类实现了IPerson接口,如果我们想在say()被调用时记录下调用的时间,最粗暴的方式就是在say()中直接增加时间记录的方法,但是如果项目中有很多类都实现了IPerson那么就需要修改很多类,而且并不是所有类都需要记录调用时间,那该怎么处理呢?肯定是使用代理啊。

    3、静态代理

    public interface IPerson {
            void say();
        }
    
    
        public class Student implements IPerson {
    
            @Override
            public void say() {
                Log.e("TAG", "Student Say");
            }
        }
    
    
        public class StudentProxy implements IPerson {
    
            private IPerson target;
    
            public IPerson getTarget() {
                return target;
            }
    
            public void setTarget(IPerson target) {
                this.target = target;
            }
    
            @Override
            public void say() {
                Log.e("TAG", "记录目标对象调用say的时间");
                target.say();
            }
        }
    

    我们需要创建一个代理类StudentProxy同样需要实现接口IPerson,将要代理的对象传进来,这样就可以不需要修改目标对象Studentsay()方法来实现我们的需求了。这就是静态代理,由于静态代理类和接口是一一对应的,如果有多个接口需要代理的话,就需要创建多个代理类,所以动态代理就应运而生。

    静态代理的特点

    • 1、一个静态代理类只能对应一种目标对象,即静态代理类和接口一一对应。
    • 2、如果有多个接口需要代理,就需要创建多个静态代理类对象。

    4、动态代理

    4.1、动态代理的原理

    • 1、设计动态代理类时,不需要显式的实现和目标类对象相同的接口。而是将这种实现推迟到运行时进行实现。
    • 2、调用动态代理类对象的方法时,会通过反射机制method.invoke(),从而自动调用目标类对象的方法。

    4.2、使用步骤

    • 1、声明 调用处理器类
    • 2、声明 目标对象类的接口
    • 3、声明 目标对象类
    • 4、通过动态代理对象调用目标对象的方法
      步骤1:声明 调用处理器类
    /**
     * 作用:
     * 1、生成 动态代理类对象
     * 2、指定 代理对象调用目标对象方法前要完成的任务
     * 注:需要实现调用处理器接口即InvocationHandler,故该类是调用处理器类
     */
    public class InvocationHandlerImpl implements InvocationHandler {
    
        //声明 被代理对象
        //作用:指定该对象类实现的一组接口中,那些方法被调用时,执行invoke
        private Object proxyObject;
    
        public Object newInstance(Object proxyObject) {
            this.proxyObject = proxyObject;
            return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);
            //Proxy.newProxyInstance() 根据指定的类加载器、目标类实现的一组接口和调用处理器类 创建动态代理类对象
            //参数1:类加载器 需要和目标类对象为同一个类加载器
            //参数2:一组接口 目标类实现的接口,动态代理类会默认实现这组接口
            //参数3:调用处理器类
        }
    
    
        /**
         * 动态代理类对象调用目标类方法前会调用invoke()
         * @param proxy 动态代理对象
         * @param method 目标对象被调用的方法
         * @param args 目标对象被调用方法的参数
         * @return 目标对象被调用方法的返回值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result=null;
            Log.e("Proxy","目标方法调用前");
            //通过反射调用目标类对象方法
            result=method.invoke(proxyObject,args);
            Log.e("Proxy","目标方法调用后");
            return result;
        }
    }
    

    步骤二:声明目标类要实现的接口

    /**
     * 目标对象类要实现的接口
     */
    public interface Subject {
    
        //定义目标对象类的接口方法
        public void buy(String name);
    
    }
    

    步骤三:声明目标类

    /**
     * 声明目标对象类并实现接口
     */
    public class RealObject1 implements Subject {
        @Override
        public void buy(String name) {
            Log.e("Proxy",name+"想买电脑");
        }
    
    }
    
    public class RealObject2 implements Subject {
        @Override
        public void buy(String name) {
            Log.e("Proxy", name+"想买平板");
        }
    }
    
    

    步骤四:通过动态代理对象调用目标类方法

    InvocationHandlerImpl dynamicProxy = new InvocationHandlerImpl();
            RealObject1 realObject1 = new RealObject1();
            Subject realObject1Proxy = (Subject) dynamicProxy.newInstance(realObject1);
            realObject1Proxy.buy("小红");
    
            RealObject2 realObject2 = new RealObject2();
            Subject realObject2Proxy = (Subject) dynamicProxy.newInstance(realObject2);
            realObject2Proxy.buy("小兰");
    

    5、源码解析

    看到这里会有如下2个疑问:

    • 1、动态代理类对象是如何生成的?
    • 2、如何通过调用动态代理类对象,来调用目标类对象?

    5.1、动态代理类和对象是如何动态生成的

    Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this)
    

    上面是创建动态代理类对象的代码,我们就从Proxy.newProxyInstance()入手

     private final static Class[] constructorParams =
            { InvocationHandler.class };
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
    
            //通过类加载器(和目标类对象同一个类加载器)和一组接口(目标类实现的接口)创建动态代理类
            Class<?> cl = getProxyClass0(loader, interfaces);
    
            try {
                //通过反射获取动态代理类的带参(参数类型为InvocationHandler)构造函数
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                //通过动态代理类的构造函数,创建动态代理类对象,参数为h
                return newInstance(cons, h);
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            }
        }
    

    该方法主要做了如下几件事:

    • 1、根据类加载器和一组接口创建动态代理类
    • 2、获取动态类的带参构造函数
    • 3、反射创建动态代理类对象

    5.2、如何通过调用动态代理对象方法来调用目标类方法

    //使用代码
    realObject1Proxy.buy("小红");
    

    下面直接看动态代理类的实现

    
    /**
     * 动态代理类的实现
     */
    public class ProxySubject extends Proxy implements Subject {
    
        //构造函数 在动态代理类的创建时获取带有一个参数(调用处理器类型)的构造函数就是这个
        protected ProxySubject(InvocationHandler h) {
            super(h);
        }
    
        //buy是目标类实现接口(Subject)中的方法,所以动态代理类也需要实现该接口
    
        @Override
        public final void buy(String name) {
            try {
                //该方法实际上调用了父类Proxy类中的h的invoke方法
                //h即是在Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);中传入的第三个参数InvocationHandler对象
                //即调用了调用处理器的InvocationHandler.invoke()
                //而在复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
                //从而调用目标对象的的方法
                super.h.invoke(this, method, args);
                return;
            } catch (Error e) {
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    

    总结:

    • 1、动态代理类实现了和目标类相同的接口,并实现了接口的方法
    • 2、方法中的实现逻辑为:通过super.h.invoke()调用InvocationHandlerinvoke()方法
    • 3、InvocationHandler中的invoke中通过反射调用目标类的方法。

    相关文章

      网友评论

          本文标题:代理模式

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