美文网首页
Java中的代理模式

Java中的代理模式

作者: 几行代码 | 来源:发表于2019-02-11 17:42 被阅读0次

    因为Retrofit剖析源码的时候会用到ava中的代理模式,所以这篇就先回忆一下代理设计模式。
    代理模式分为两种:
    代理模式解释:为其他对象提供一种代理,用以控制对这个对象的访问。(直白一点就是代替别人去做一些我们想要做的事情)
    1.静态代理(抽象类)


    image.png

    代码实现:

    /**
     * 被目标对象和代理对象同时继承的抽象类
     */
    public abstract class AbstractObject {
        protected abstract void operation(); // 经营预算
    }
    

    被代理对象:

    /**
     * 目标对象 (被代理对象)
     */
    public class RealObject extends AbstractObject{
    
        private static final String TAG = RealObject.class.getSimpleName();
    
        @Override
        public void operation() {
            Log.e(TAG,"do operation......");
        }
    }
    

    代理对象:

    /**
     * 代理类
     */
    public class ProxyObject extends AbstractObject {
    
        private static final String TAG = ProxyObject.class.getSimpleName();
        // 对目标类的引用
        private RealObject mRealObject;
    
        public ProxyObject(RealObject mRealObject) {
            this.mRealObject = mRealObject;
        }
    
        @Override
        public void operation() {
            Log.e(TAG,"do something before real operation......");
            if (mRealObject == null) {
                mRealObject = new RealObject();
            }
            mRealObject.operation();
            Log.e(TAG,"do something after real operation......");
        }
    }
    

    测试:

    public static void main(String[] args) {
            ProxyObject proxyObject = new ProxyObject(new RealObject());
            proxyObject.operation();
        }
    
    do something before real operation......
    do operation......
    do something after real operation......
    

    2.动态代理(JDK实现/CJLib继承)

    • 概念解释:代理类在程序运行时创建的代理方式。
    • 相比于静态代理:动态代理可以很方便的去改变代理类的函数进行统一的处理,而不用去修改每一个代理类的函数,根据你相应的业务逻辑
    • 优点:无侵入式的扩展代码

    JDK动态代理:Java内部的反射机制来实现的,这个机制在生成类的时候比较高效。(必须要针对接口进行代理)

    • 在不改变原来代码的情况下,在调用方法的前后,插入我们自己想要的逻辑。(例子)

    • 动态代理和静态代理最大的不同,动态代理的代理类是不需要手动生成的,是根据我们的配置在运行时动态生成的。(是在运行时生成的代理)

    • 需要用到InvocationHandler接口和Proxy类

    代码示例:
    被目标对象和代理对象都要实现的接口

    /**
     * 动态代理
     * 被目标对象和代理对象都要实现的接口
     */
    public interface InterfaceObject {
        void shopping();
    }
    

    被代理类 (目标类):

    /**
     * 动态代理
     * 被代理类 (目标类)
     */
    public class BeRepresent implements InterfaceObject {
        private static final String TAG = BeRepresent.class.getSimpleName();
        @Override
        public void shopping() {
            System.out.println("买点东西回来");
        }
    }
    

    代理类是由JDK动态创建的:

    /**
     * 动态代理
     * 代理类:每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现
     * 当使用者调用了代理对象,所实现的代理对象的接口时,这个调用的信息就会传递到InvocationHandler
     * 中的invoke方法中
     */
    public class ProxyF implements InvocationHandler {
    
        private static final String TAG = ProxyF.class.getSimpleName();
        private Object target; // 要代理的真实对象
    
        public ProxyF(Object target) {
            this.target = target;
        }
    
        /**
         * 相当于一个拦截的方法
         * @param proxy   代理对象
         * @param method  代理方法
         * @param objects 代理方法中的参数
         * @return 返回值会返回给使用者
         * @throws Throwable 异常
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
            System.out.println("代理对象:" + proxy.getClass().getName());
            System.out.println("代理方法中的参数:" + (objects != null && objects.length > 0 ? objects[0] : null));
            System.out.println("代理方法:" + method);
            System.out.println(" before ");
            method.invoke(target,objects);
            System.out.println(" after ");
        }
    }
    

    测试:

    public static void main(String[] args) {
            InterfaceObject mBeRepresent = new BeRepresent();
            ProxyF proxy = new ProxyF(mBeRepresent);
            // java.lang.reflect.Proxy.newProxyInstance(....)方法获得真实对象的代理对象 (反射)
            InterfaceObject mInterfaceObject = (InterfaceObject) Proxy.newProxyInstance(mBeRepresent.getClass().getClassLoader()
                    , mBeRepresent.getClass().getInterfaces(), proxy);
            // 通过代理对象调用真实对象相关接口中实现的方法,这个时候就会跳转到这个代理对象所关联的InvocationHandler
            // 的invoke方法中
            mInterfaceObject.shopping();
            // 获得真实对象的代理对象所对应的class对象的名称,用字符串表示
            System.out.println(mInterfaceObject.getClass().getName());
    }
    

    结果:

    代理对象:com.sun.proxy.$Proxy0
    代理方法中的参数:null
    代理方法:public abstract void dynamicproxy.InterfaceObject.shopping()
     before 
    买点东西回来
     after 
    com.sun.proxy.$Proxy0
    

    注意:被代理对象的类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。
    总结:
    JDK实现原理:

    1.拿到被代理对象的引用,然后获取它实现的接口
    2.JDK重新生成一个类,同时实现获取到被代理对象所实现的接口
    3.在代理类的静态构造块中,代理类通过反射获取了被代理类的详细信息
    4.重新生成一个class字节码
    5.然后编译
    JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

    还有一种是CGlib实现的动态代理:
    cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

    静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
    JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
    CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

    最后,抛出两个问题:

    1.retrofit为什么要使用动态代理 ?
    答案:
    因为1个静态代理只服务1种类型的目标对象,若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象 ,在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂的问题,而动态代理只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码 更强的灵活性设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由JVM来实现,在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化。

    2.动态代理的缺点是什么?
    答案:
    效率低
    相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法。

    应用场景局限
    因为Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类即只能动态代理 实现了接口的类。

    相关文章

      网友评论

          本文标题:Java中的代理模式

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