• 设计模式8:代理模式-动态代理


    上一篇:设计模式8:代理模式-静态代理

    如何理解“动态”这两个字?

    “动态”的含义是代码生成代理类,一个代理类可以代理多个接口

    动态区别于死板静态代理中一个代理类只能代理一个接口,其他不同的接口,就需要再手写不同的代理类,这就很死板

    动态代理类似于在安卓里面,我们常说的动态申请权限,其实就是用java或kotlin代码申请权限,而不是在AndroidManifest.xml文件里面写死一次可以申请一个或多个权限

    上一篇:中,StationProxyStarProxy都是我们手写的代理类。动态代理可以自动生成代理类。

    动态代理简单的代码实例

    动态代理需要jdk中提供的两个类InvocationHandlerProxy,重写InvocationHandler接口实现代理方法,Proxy负责生成代理对象。
    上篇文章中“明星代理”的例子,用动态代理实现如下:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    interface IStar {
        void sing(double money);
    }
    
    class StarImpl implements IStar {
        public void sing(double money) {
            System.out.println("唱歌,收入" + money + "元");
        }
    }
    
    class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("请先预约时间");
            System.out.println("沟通出场费用");
            double money= (double) args[0];
            if (money < 100000) {
                System.out.println("对不起,出场费10w万以内不受理");
                return null;
            }
    
            System.out.println("经纪人抽取了"+ money * 0.2 + "代理费用");
            args[0] = money * 0.8;
            Object result = method.invoke(target, args);
            System.out.println("演唱完毕");
            return result;
        }
    }
    
    public class DynamicProxyExample {
        public static void main(String[] args) {
            //原始对象
            IStar mStar = new StarImpl();
            InvocationHandler handler = new MyInvocationHandler(mStar);
    
            //代理对象
            IStar proxyHello = (IStar) Proxy.newProxyInstance(
                    mStar.getClass().getClassLoader(),
                    mStar.getClass().getInterfaces(),
                    handler
            );
    
            proxyHello.sing(200000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    运行结果:

    请先预约时间
    沟通出场费用
    经纪人抽取了40000.0代理费用
    唱歌,收入160000.0元
    演唱完毕
    
    • 1
    • 2
    • 3
    • 4
    • 5

    一个InvocationHandler代理多个接口

    动态代理的核心优势就在于,一个代理类,可以代理多个接口。如下,演示的是一个代理handler,同时代理两个接口InterfaceA和InterfaceB:

    package dynamic_proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    interface InterfaceA {
        void methodA();
    }
    
    interface InterfaceB {
        void methodB();
    }
    
    class ClassA implements InterfaceA {
        public void methodA() {
            System.out.println("执行methodA");
        }
    }
    
    class ClassB implements InterfaceB {
        public void methodB() {
            System.out.println("执行methodB");
        }
    }
    
    class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("methodA")) {
                System.out.println("调用methodA之前");
                Object result = method.invoke(target, args);
                System.out.println("调用methodA之后");
                return result;
            } else if (method.getName().equals("methodB")) {
                System.out.println("调用methodB之前");
                Object result = method.invoke(target, args);
                System.out.println("调用methodB之后");
                return result;
            } else {
                throw new UnsupportedOperationException("Unsupported method: " + method.getName());
            }
        }
    }
    
    public class DynamicProxyExample {
        public static void main(String[] args) {
    
    
            InvocationHandler handlerA = new MyInvocationHandler(new ClassA());
    
            InterfaceA proxyA = (InterfaceA) Proxy.newProxyInstance(
                    handlerA.getClass().getClassLoader(),
                    new Class[]{InterfaceA.class},
                    handlerA
            );
            proxyA.methodA();
    
            InvocationHandler handlerB = new MyInvocationHandler(new ClassB());
            InterfaceB proxyB = (InterfaceB) Proxy.newProxyInstance(
                    handlerB.getClass().getClassLoader(),
                    new Class[]{InterfaceB.class},
                    handlerB
            );
            proxyB.methodB();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    代码执行结果:

    调用methodA之前
    执行methodA
    调用methodA之后
    调用methodB之前
    执行methodB
    调用methodB之后
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    动态代理大揭秘,带你彻底弄清楚动态代理!

    有动态代理,为什么还要用Cglib代理?

    动态代理只能把代理对象,赋值给接口,如上面的例子。不能把代理对象直接赋值一个普通类。而Cglib代理可以做到。具体怎么做的,下篇文章再讲。

  • 相关阅读:
    VAE(变分自动编码器)
    《逆向工程核心原理》学习笔记(七):反调试技术
    第一次写计算机论文无从下手怎么办?(一) - 易智编译easeediting
    java-php-python-会议查询系统计算机毕业设计
    【Mysql备份】Python脚本备份docker的MySQL数据
    联想小新 win10电脑系统安装教程
    WPF_布局基础
    【Java综合专栏】「渐入佳境」全链路追踪原理之Java Agent探针的底层运作原理和分析(中篇)
    计算机网络
    龙芯 Loongson 架构 UOS 系统编译 Qt 5.15.2 源码
  • 原文地址:https://blog.csdn.net/zhangjin1120/article/details/132654735