48 | 代理模式:代理在RPC、缓存、监控等场景中的应用

2020-02-21 王争
《设计模式之美》
课程介绍


讲述:冯永吉

时长:大小8.57M


前面几节,我们学习了设计模式中的创建型模式。创建型模式主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。
其中,单例模式用来创建全局唯一的对象。工厂模式用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。原型模式针对创建成本比较大的对象,利用对已有对象进行复制的方式进行创建,以达到节省创建时间的目的。
从今天起,我们开始学习另外一种类型的设计模式:结构型模式。结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题。结构型模式包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式。今天我们要讲其中的代理模式。它也是在实际开发中经常被用到的一种设计模式。
话不多说,让我们正式开始今天的学习吧!

代理模式的原理解析

代理模式(Proxy Design Pattern...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 小兵
    2020-02-23
    组合模式的优点在于更加灵活,对于接口的所有子类都可以代理,缺点在于不需要扩展的方法也需要进行代理。
    继承模式的优点在于只需要针对需要扩展的方法进行代理,缺点在于只能针对单一父类进行代理。
    7
    128
  • ,
    2020-02-21
    java中,动态代理的实现基于字节码生成技术(代码里就是newProxyInstance片段),可以在jvm运行时动态生成和加载字节码,类似的技术还有asm,cglib,javassist,平时编译java用的javac命令就是字节码生成技术的"老祖宗"
         java中用到字节码生成技术的还有JSP编译器.AOP框架,反射等等
         深入理解java虚拟机第三版里对动态代理的描述:
         动态代理中所说的"动态",是针对使用Java代码实际编写了代理类的"静态"代理而言的,它的优势不在于省去了编写代理类那一点编码工作量,而是实现了可以在原始类和接口还未知的时候,就确定了代理类的行为,当代理类与原始类脱离直接联系后,就可以很灵活的重用于不同的应用场景之中
         
    展开
    1
    80
  • trier
    2020-04-11
    粗略“翻译”至PHP,中间省略了很多关键的判断,主要是想知道有多少PHPer在看。

    interface IUserController
    {
        public function login(String $telephone, String $password);
        public function register(String $telephone, String $password);
    }
      
    class UserController implements IUserController
    {
        public function login(String $telephone, String $password)
        {
            echo 'is Login' . PHP_EOL;
        }
      
        public function register(String $telephone, String $password)
        {

        }
    }

    class MetricsCollector
    {
        public function recordRequest($requestInfo)
        {
        }
    }

    class RequestInfo
    {
        public function __construct($apiName, $responseTime, $startTimestamp)
        {
        }
    }


    class MetricsCollectorProxy
    {
        private $proxiedObject;

        private $metricsCollector;

        public function __construct(MetricsCollector $metricsCollector)
        {
            $this->metricsCollector = $metricsCollector;
        }
        
        public function createProxy(object $object)
        {
            $this->proxiedObject = $object;
            return $this;
        }

        public function __call($method, $arguments)
        {
            $ref = new ReflectionClass($this->proxiedObject);
            if (!$ref->hasMethod($method))
                throw new Exception("method not existed");

            $method = $ref->getMethod($method);
            $startTimestamp = time();
            $userVo = $this->callMethod($method, $arguments);
            $endTimeStamp = time();
            $responseTime = $endTimeStamp - $startTimestamp;
            $requestInfo = new RequestInfo("login", $responseTime, $startTimestamp);
            $this->metricsCollector->recordRequest($requestInfo);

            return $userVo;
        }


        private function callMethod(\ReflectionMethod $method, $arguments)
        {
            //前置判断省略
            $method->invokeArgs($this->proxiedObject, $arguments);
        }

    }


    $proxy = new MetricsCollectorProxy(new MetricsCollector);
    $userController = $proxy->createProxy(new UserController);
    $userController->login(13800138000, 'pwd');
    展开
    6
    21
  • LJK
    2020-02-21
    是时候展示我动态语言Python的彪悍了,通过__getattribute__和闭包的配合实现,其中有个注意点就是在获取target时不能使用self.target,不然会递归调用self.__getattribute__导致堆栈溢出:
    class RealClass(object):
        def realFunc(self, s):
            print(f"Real func is coming {s}")

    class DynamicProxy(object):
        def __init__(self, target):
            self.target = target
        
        def __getattribute__(self, name):
            target = object.__getattribute__(self, "target")
            attr = object.__getattribute__(target, name)
            
            def newAttr(*args, **kwargs):
                print("Before Calling Func")
                res = attr(*args, **kwargs)
                print("After Calling Func")
                return res
            return newAttr
    展开
    5
    19
  • 辣么大
    2020-02-25
    感谢争哥,今天终于学会了“动态代理”
    还是要动手试试,代码在这是 https://bit.ly/37UqLNf

    学有余力的小伙伴,附上一些资料吧:
    https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
    https://www.baeldung.com/java-dynamic-proxies
    展开
    3
    10
  • 迷羊
    2020-02-22
    Java中的动态代理原理就是运行的时候通过asm在内存中生成一份字节码,而这个字节码就是代理类的字节码,通过System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");设置可以保存这份字节码,反编译后看下其源码就知道Java中的动态代理是什么原理了。
    
    10
  • 不似旧日
    2020-02-24
    笔记:

    - 什么是代理模式:它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。

    - 代理模式得实现:
      - 静态代理
        1. 实现被代理对象口: 要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。
        2. 继承被代理对象:代理类继承原始类,然后扩展附加功能。
      - 动态代理 : 在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
        1. jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
        2. cglib动态代理是利用asm开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
    展开
    1
    9
  • Eden Ma
    2020-02-21
    1、OC中通过runtime和分类来实现动态代理.
    2、组合优势可以直接使用原始类实例,继承要通过代理类实例来操作,可能会导致有人用原始类有人用代理类.而继承可以不改变原始类代码来使用.
    3
    9
  • webmin
    2020-02-21
    1. .net支持反射和动态代理,所以实现方式和java类似;golang目前看到的都是习惯使用代码生成的方式来达成,根据已有代码生成一份加壳代码,调用方使用加壳代码的方法,例好:easyJson给类加上序列化和反序列化功能;gomock生成mock代理。
    2. 组合与继承的优缺点:
    没有绝对的优缺点,要看场景比如:
    当被代理的类所有功能都需要被代理时,使用继承方式就可以编译器检查(被代理类修改时编译期就可以检查出问题);
    当被代理的类只是部分功能需要被代理时,使用组合方式就可按需代理,但是如果原来不需要的,后来也需要了就比较尴尬了。
    继承可能会让代理类被迫实现一些对代理类来说无意义代码,继承方式对代理类的侵入比较大,而组合的侵入影响比继承可控。
    展开
    
    8
  • J.Smile
    2020-02-21
    动态代理有两种:jdk动态代理和cglib动态代理。
    3
    6
  • distdev
    2020-02-24
    请问 如果对于业务方法 有多个非业务功能 比如metrics, logging还有其他的 应该实现在一个代理class里?还是一个filter chain里?
    1
    5
  • 小晏子
    2020-02-21
    C#中可以通过emit技术实现动态代理。
    基于继承的代理适合代理第三方类,jdk中的动态代理只能代理基于接口实现的类,无法代理不是基于接口实现的类。所以在spring中有提供基于jdk实现的动态代理和基于cglib实现的动态代理。
    
    5
  • Geek_35cfdd
    2020-09-09
    动态代码虽然对使用场景讲了,先不说自己如何实现一个动态代码没有讲。就算拿java的现成接口,是不是最起码应该讲下里面的实现。做到知其然知其所以然呢?你这动态代理讲的就像一个hello world。
    
    3
  • 放个屁臭到了自己
    2020-08-03
    为啥我感觉静态代理像是装饰器模式
    1
    3
  • 菜鸟小白
    2020-05-26
    代理顾名思义 就是对要调用的方法再次封装到一个新类中,这个类会调用你需要目标方法。
    一、
    function a(){//要做的事}
    function 代理(){
    //你可以在你要代理的方法前加逻辑
    a();
    //也可以在方法后面加
    }
    调用 代理()
    二、动态代理
    所有要被代理的类都是用同一个规范。
    比方说都用invoke(){具体逻辑}
    a.invoke
    b.invoke
    c.invoke
    .....这样写太麻烦。
    可以在动态代理类中放一个list<string>变量
    用来存存储要代理的类名

    动态代理里面调用方法
    public void invoke(){
        迭代list
        {
         反射 创建被代理的具体类
        具体类.invoke
        }
    }

    展开
    
    2
  • lcf枫
    2020-03-22
    目前看到这里有点懵 分不清代理和装饰器了
    5
    2
  • Heaven
    2020-03-10

    今天我回答下第二个问题,在原有的JDK中的代理,是一种基于接口的代理模式,要求代理类只能去代理某些接口,于是乎,为了能直接代理一个代理类,cglib加入了,可以传入一个对象实例,来进行包裹,从而达到代理这种需求,两者实际来说,JDK的代理类创建时间比cglib快的太多了,于是乎更加适合在框架中定义为非单例的类去代理,cglib适合于生成单例式类的代理类
    
    2
  • 阿德
    2020-03-09
    动态代理那里的invoke方法的第一个参数proxy有什么用,一直搞不清楚
    2
    2
  • 小文同学
    2020-03-07
    继承的代理类是通过重写来实现代理,如果超类本身方法间会调用,则被新增的代码也会被重复调用。这其实已经是一种修改,不是一种扩展了。相比下,组合型代理就没有这个问题

    组合型代理需要原来的类有接口(抽象类),代理类可以实现接口,来通过组合构成代理。继承型代理则没这个问题。
    
    2
  • Summer 空城
    2020-02-21
    老师好,有个地方不太明白,请指点下。
    Spring框架实现AOP的时候是在BeanFactory中生成bean的时候触发动态代理替换成代理类的么?
    如果我们自己想对某个Controller做代理的时候要怎么处理呢?一般是用@Controller注解某个Controller的,而且这个Controller不会实现接口。
    谢谢老师!
    5
    2