zoukankan      html  css  js  c++  java
  • 一文看懂什么是代理模式【没有废话,很硬】

    写在前面

    讲真,我对设计模式其实并没有很深入的了解。这里我想把自己的理解 通过文字的方式表达出来。为啥突然想起写这个文章呢?是因为我在星球里面看见有人提问:
    在这里插入图片描述
    其实,我在去年的时候也写过相关的内容,但是没有展开。这里我就一一把自己所知写出来。
    在这里插入图片描述

    那么接下来就开始吧,如果有什么错误的地方,欢迎指正~我也还在不断的学习中,大家一起加油!

    核心:原有对象需要额外的功能,就可以使用代理这项技术

    在这里插入图片描述
    代理模式两大类:

    • 静态代理
      透明代理
    • 动态代理
      基于接口的JDK动态代理
      基于类的CgLib动态代理

    01静态代理

    目的:一开始就要抓住我们是去 增强 类的功能的。

    所谓静态代理,大致就是需要重新写一个 代理类 去实现接口,然后这个类的构造方法 用待增强类 作为入参。在实现接口的方法中,调用 待增强类 对应的方法。同时,在这个方法的前后可以调用其余的方法,从而实现功能增强的目的。

    什么?!没看懂,没关系,我们上代码

    1.1 来一个接口:

    public interface Subject {
    
         void doSomeThing();
    }
    
    

    1.2 实现这个接口 (就像web里面的 接口+Impl)

    public class RealSubject implements Subject {
        public void doSomeThing() {
            System.out.println("我是真正的实现类,这里可能有大量的逻辑处理代码 doSomeThing()");
            
        }
    }
    

    其实到这里,就是我写代码时候的常规操作。感觉很正常嘛,写一个接口,然后再写实现类去实现这个接口定义的方法。需要使用的时候:直接

    接口 接口1 = new 接口实现类();
    接口1.doSomeThing();
    

    当然这样,也是没错,OK 。

    随着业务庞大,你就会知道,拥有一个 proxy 类对真实类的封装对于粒度的控制有着重要的意义?

    1.3 写一个 代理类
    这个类需要去实现我们的接口,同时将上面的 实现类作为入参 传递给代理类的 构造函数。

    注意:我们就是在这里实现类功能的增强的!

    /**
     * Description: 静态代理
     *
     * 
     * 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,
     * 代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题。
     *
     * 缺点:如果目标对象的接口有很多方法的话,那我们还是得一一实现,这样就会比较麻烦
     *
     * @Author: 留歌36
     * @Date: 2020/5/21 11:44
     */
    public class SubjectProxy implements Subject{
    
        // 真正的实现类
        private RealSubject realSubject;
    
        // 构造方法1
        public SubjectProxy(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        // // 构造方法2: 只做  RealSubject  的代理,且RealSubject对外界透明,也叫做透明代理
        public SubjectProxy() {
            this.realSubject = new RealSubject();
        }
    
        /**
         *  实现接口的方法
         */
        public void doSomeThing() {
    
            // 这个方法 之前之后 还可以做一些其他的事情,就实现了类功能的增强
            SubjectProxy.increaseBefore();
    
            // 通过这个静态代理就可以使用一句代码顶真正实现类 的功能实现
            realSubject.doSomeThing();
    
    
            increaseAfter();
    
        }
    
    
        // 1.在方法之前做增强
        private static void increaseBefore() {
            System.out.println("1.在方法之前做增强");
        }
    
        // 2.在方法之后做增强
        void increaseAfter() {
            System.out.println("2.在方法之后做增强");
        }
    
    
    }
    
    

    其实到这里,静态代理就结束了,当然我们得写一个类测试一下撒:
    在这里插入图片描述

    补充:所谓透明代理,就是将我们的真实的 实现类 封装进了代理里面,对于外界来说是透明的。就像:

       public SubjectProxy() {
            this.realSubject = new RealSubject();
        }
    
    

    关于静态代理就是这样了,其实就是写一个代理类去实现目标对象的接口。

    缺点:如果 目标对象的接口 有很多方法的话,那我们还是得一一实现,这样就会比较麻烦,所以引入动态代理来解决此类问题。

    02动态代理

    抓住一点:静态代理我们是手动写一个 代理类 去实现目标对象的接口,然后一一实现这些接口。这样就太难搞了,,,然后动态代理就不用一一去手动实现,我们利用 反射 技术实现这一目的。

    Java提供了一个Proxy类,调用它的newInstance方法 ,该方法 返回指定接口的代理类的实例 将方法调用 分派到 指定的调用 处理程序。我

    们只需要写一个动态代理的类,就直接齐活了。是不是很方便~

    public class ProxyHandler implements InvocationHandler {
    
        private Object tar;
    
        // 绑定目标对象,并返回 代理对象 【这在之前我们是手写的】
        public Object bind(Object tar)
        {
            this.tar = tar;
    
            /**
             * 参数一:生成代理对象使用哪个类装载器【一般我们使用的是被代理类的装载器】
             *
             * 参数二:生成哪个对象的代理对象,通过接口指定【指定要被代理类的接口】
             *
             * 参数三:生成的代理对象的方法里干什么事【实现handler接口,我们想怎么实现就怎么实现】
             * 
    
    			注意:代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口,代理对象会实现接口的所有方法】
             */
           
            return Proxy.newProxyInstance(
    
                    tar.getClass().getClassLoader(),
    
                    tar.getClass().getInterfaces(),
    
                    this // 这里的this,即上面定义的Object,代表实现的所有方法 交由Object 的 invoke() 反射方法进行处理
            );
        }
    
        // 用户调用代理对象的什么方法,都是在调用处理器的invoke方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object result = null;
    
            //这里就可以进行所谓的AOP编程了,不管你调用 生成对象的什么方法,invoke都是会触发,这里就可以做一些前置或后置的方法
            // increaseBefore() 方法
    
            //在调用具体函数方法前,执行功能处理【这里就是调用生成对象的 具体方法】
            result = method.invoke(tar,args);
    
            // increaseAfter() 方法
    
            //在调用具体函数方法后,执行功能处理
            return result;
    
        }
    }
    
    

    通过动态代理实现的全部方法,全都是通过invoke()方法调用
    在这里插入图片描述

    这里,我在代码中也写了大量的说明,只要知道,我们传入一个 目标对象的实例 ,就能得到一个 动态生成的代理类,这个类拥有 传入的目标对象实例的全部 默认实现方法。

    在调用这个目标对象实例的方法的时候,会触发invoke()中定义的增强功能。

    代理对象的生成,是利用 JDK API,动态地在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型),并且会默认实现接口的全部方法。

    补充: 基于接口的JDK动态代理 && 基于类的CgLib动态代理
    在这里插入图片描述
    上面是我小伙伴总结的,我觉得写得不错,就贴过来了,欢迎关注他的博客地址

    说了那么多,可是真正回归到我们日常写代码中:

    1. 我知道Spring的AOP(面向切面编程)底层 其实就是动态代理来实现的,其实也没有很熟悉

    2. 讲真,我就不大知道了,后面我有知道,再回来补充

    然后差不多,代理模式就写到这里吧。

    核心:原有对象需要 增强 功能,就可以使用代理这项技术

    下一篇写装饰器模式

  • 相关阅读:
    369. Plus One Linked List
    147. Insertion Sort List
    817. Linked List Components
    61. Rotate List
    Object 类
    多态
    重写方法
    Protected 修饰符
    继承
    数组
  • 原文地址:https://www.cnblogs.com/liuge36/p/13019735.html
Copyright © 2011-2022 走看看