• 动态代理入门


    1. 一个方法

    1. 该方法的作用: 在运行时, 动态创建一组指定接口的实现类对象!
    2. 动态代理的作用:学习 AOP(面向切面编程)
    3. JavaSE java.lang.reflect

    // 该方法的作用, 动态创建实现了 interfaces 数组中所有指定接口的实现类对象!
    Object proxyObject = Proxy.newProxyInstance(ClassLoader classLoader, Class[] interfaces,
                                                InvocationHandler h);
    
    // 参数介绍
        /*
         * ClassLoader: 类加载器
         *         作用: 用来加载类的, 把 .class 文件加载到内存中, 形成 Class 对象!
         * Class[] interfaces : 要实现的接口们
         *         
         * InvocationHandler h
         *   它是调用处理器;
         *   代理对象的所有方法(个别不执行), 都会调用 InvocationHandler 的 invoke() 方法
         */
    
    // 示例
        public class Demo {
            public void fun(){
    
                // 该方法需要动态生成一个类,这个类实现了 A, B 接口,然后创建这个类的对象!
                // 需要生成一个类,而这个类也需要加载到方法区中, 需要使用 ClassLoader 来加载
    
                // 使用 Demo 类的类加载器
                ClassLoader loader = this.getClass().getClassLoader();
    
                // 调用处理器
                InvocationHandler h = new InvocationHandler(){
                    public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            System.out.println("这是动态代理......");
                            return null;
                    }
                }
    
                // 使用三大参数创建代理对象!
                // 代理对象实现了所有接口中的方法, 方法的内容都是调用 InvocationHandler 的 invoke 方法
                Object o = Proxy.newProxyInstance(loader, new Class[]{ A.class, B.class}, h);
    
                // 将 o 类型强转为 A 和 B 类型
                A a = (A)o;
                B b = (B)o;
    
                // 调用代理对象中的 a 和 b 方法
                a.a();
                b.b();
                Object result = a.aaa("hello",100);
                System.out.println(result);
            }
        }
    
        // 两个接口
        interface A{
            public void a();
            public Object aaa(String s, int i);
        }
        interface B{
            public void b();
        }
    

    2. InvocationHandler 接口

    1. 该接口只有一个 invoke 方法. 调用代理对象所实现接口中的方法,就是调用 invoke 中的内容.
      • public Object invoke(Object proxy, Method method, Object[] args);
      • Object proxy: 表示当前对象,即代理对象! 在调用谁的方法!
      • Method method: 当前被调用的方法(目标方法)
      • Object[] args: 实参, 表示将调用方法中的参数赋给该参数

    3. 动态代理的应用

    3.1 增强的手段
    1. 继承
      • 被增强的对象不能变
      • 增强的内容不能变
    2. 装饰者模式
      • 被增强的对象可变
      • 增强的内容不能变
    3. 动态代理
      • 被增强的对象可变
      • 增强的内容可变
    3.2 应用示例
    1. 目标对象: 被增强的对象;
    2. 代理对象: 需要目标对象,然后在目标对象上添加了增强方法后的对象;
    3. 目标方法: 增强的内容;
    4. 代理对象 = 目标对象 + 增强
    // 示例一
        // 服务员接口
        public interface Waiter{
            // 服务方法
            public void serve();
        }
    
        // 服务员实现类
        public class ManWaiter implements Waiter{
            public void serve(){
                System.out.println("服务中......");
            }
        }
    
        // 调用服务员实现类中的方法
        public class Demo {
            public void fun(){
                Waiter waiter = new ManWaiter();
                waiter.serve();
            }
        }
    
    // 升级版本
    //   使用动态代理给服务员添加功能
        public class Demo2 {
            public void fun(){
                Waiter manWaiter = new ManWaiter();
    
                // 给出三大参数, 来创建方法,得到代理对象
                ClassLoader loader = this.getClass().getClassLoader();
                Class[] interfaces = {Waiter.class};
                // 参数 manWaiter 表示目标对象
                InvocationHandler h = new WaiterInvocationHandler(manWaiter);
    
                // 得到代理对象
                Waiter waiterProxy = (Waiter)Proxy.newProxyInstance(loader,interfaces,h);
    
                waterProxy.serve(); // 前面添加 "您好", 后面添加 "再见"
            }
        }
    
        class WaiterInvocationHandler implements InvocationHandler{
                private Waiter waiter; // 目标对象
    
                public WaiterInvocationHandler(Waiter waiter){
                    this.waiter = waiter;
                }
    
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable{
    
                    System.out.println("您好!");
                    this.waiter.serve(); // 调用目标对象的目标方法
                    System.out.println("再见!");
                }
        }
    
    3.3 代理工厂实现
    1. 代理工厂的目标: 是让目标对象和增强都可以替换.

    2. 代理工厂使用步骤

      创建代理工厂
      给代理工厂设置三样东西

      • 目标对象: setTargetObject(xxx);
      • 前置增强: setBeforeAdvice(该接口的实现);
      • 后置增强: setAfterAdvice(该接口的实现);

      调用 createProxy() 得到代理对象

      • 执行代理对象方法时的步骤:
      • 执行 BeforeAdvice 的 before 方法
      • 执行目标对象的目标方法
      • 执行 AfterAdvice 的 after 方法
    // 示例二: 代理工厂
        public class Demo3{
    
            // 使用代理工厂类
            public void fun(){
    
                // 创建代理工厂
                ProxyFactory factory = new ProxyFactory();
    
                // 设置目标对象
                factory.setTargetObject(new ManWaiter());
    
                // 设置前置增强(此处为匿名内部类)
                factory.setBeforeAdvice(new BeforeAdvice(){
                    public void before(){
                        System.out.println("您好!");
                    }
                });
    
                // 设置后置增强
                factory.setAfterAdvice(new AfterAdvice(){
                    public void after(){
                        System.out.println("再见!");
                    }
                });
    
                // 生成代理对象
                Waiter waiter = (Waiter)factory.createProxy();
                waiter.serve();
            }
        }
    
        // 代理工厂类
        /*
         * 它用来生成代理对象
         * 它需要所有的参数
         *   目标对象
         *   增强
         */
        public class ProxyFactory{
            private Object targetObject; // 目标对象
            private BeforeAdvice beforeAdvice; // 前置增强
            private AfterAdvice afterAdvice; // 后置增强
    
            // 用来生成代理对象
            public Object createProxy(){
                // 1. 给出三大参数
                ClassLoader loader = this.getClass().getClassLoader();
    
                // 获取目标对象的接口
                Class[] interfaces = targetObject.getClass().getInterfaces();
    
                InvocationHandler h = new InvocationHandler(){
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable{
    
                        // 在调用代理对象的方法时, 会执行这里的内容
    
                        // 执行前置增强
                        if(beforeAdvice != null){
                            beforeAdvice.before();
                        }    
    
                        // 执行目标对象的目标方法
                        Object result = method.invoke(targetObject,args);
    
                        // 执行后置增强
                        if(afterAdvice != null){
                            afterAdvice.after();
                        }
    
                        // 返回目标对象的返回值
                        return result;
                    }
                };
    
                // 得到代理对象
                Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
                return proxyObject;
            }
    
            public Object getTargetObject(){
                return targetObject;
            }
            public void setTargetObject(Object targetObject){
                this.targetObject = targetObject;
            }
    
            public BeforeAdvice getBeforeAdvice(){
                return beforeAdvice;
            }
            public void setBeforeAdvice(){
                this.beforeAdvice = beforeAdvice;
            }
    
            public AfterAdvice getAfterAdvice(){
                return afterAdvice;
            }
            public void setAfterAdvice(){
                this.afterAdvice = afterAdvice;
            }
    
        }
    
        // 前置增强
        public interface BeforeAdvice{
            public void before();
        }
    
        // 后置增强
        public interface AfterAdvice{
            public void after();    
        }
    

    参考资料:

  • 相关阅读:
    CSS 实现隐藏滚动条同时又可以滚动
    在vue项目中的axios使用配置记录
    QS:vue中qs的使用
    Electron 无边框窗口最大化最小化关闭功能
    CSS样式表能否控制文字禁止选择,复制, 焦点
    yarn 在Vue框架中的常用命令
    Vue 实现左边导航栏且右边显示具体内容(element-ui)
    Vuex 存储||获取后台接口数据
    软件工程第二周开课介绍
    返回一个整数数组中最大子数组的和 (非环状 和环状)
  • 原文地址:https://www.cnblogs.com/linkworld/p/7681689.html
Copyright © 2020-2023  润新知