美文网首页
代理模式的理解,以及由此引发对设计模式的理解

代理模式的理解,以及由此引发对设计模式的理解

作者: _小毛驴 | 来源:发表于2020-12-20 14:10 被阅读0次

在看狂神讲Spring AOP的时候,又顺便讲到了代理模式。

静态代理

静态代理模式还是比较好理解的。就是在客户服务商之间,架设一个中间商。原先的逻辑是:客户直接调用服务商。现在是:客户调用中间商,间接调用服务商。这样的改动,中间商赚了差价,但是,好处是中间商可以帮我们做一些额外的工作。

动态代理

如果这样去理解:JVM在运行期间动态生成代理类。所以,就叫动态代理,所以就好,那就差点意思了。
我们在这里从结果去理解动态代理,就容易不明就里。我们不妨从静态代理的问题开始推演。

  • 客户调用中间商,这一步貌似必不可少,跑不掉了,也貌似没有可以优化的余地,先按下不表。
  • 中间商持有服务商属性,并且调用服务商的方法,这一步,其实分为两块。
    1. 中间商持有服务商属性,这是中间商起作用的必要步骤,貌似也不能省。
    2. 中间商根据客户具体调用,做额外的工作,间接调用服务商。如果这里的额外工作特别通用,比如打印日志,那么,就会非常繁琐,而且还不利于复用。这里,我们的直觉是,可不可以把这些特别通用的代码提取出来,封装在一个方法中,类似于一个拦截器,每次中间商的调用,都要经过这个拦截器,处理完我们的额外工作之后,再调用服务商
package com.keqing.staticproxy;

public class ServiceProxy implements Service{
    
    private ServiceProvider serviceProvider;
    
    public  ServiceProxy(ServiceProvider serviceProvider) {
        this.serviceProvider = serviceProvider;
    }
    
    @Override
    public void add(Object obj) {
        System.out.println("日志-调用了方法:"+"add");
        serviceProvider.add(obj);
    }

    @Override
    public void delete(Integer id) {
        System.out.println("日志-调用了方法:"+"delete");
        serviceProvider.delete(id);
    }

    @Override
    public void update(Object obj) {
        System.out.println("日志-调用了方法:"+"update");
        serviceProvider.update(obj);
    }

    @Override
    public void query() {
        System.out.println("日志-调用了方法:"+"query");
        serviceProvider.query();
    }

}

经过上面的分析。问题就水落石出了。

静态代理面临的问题:我们需要在中间商调用服务商这个过程中,切入一个拦截器方法,处理通用业务逻辑

jdk给我们提供了一整套方案。这套方案是这样的。

  • 首先定义一个拦截器,并实现拦截方法。这个拦截器需要持有服务商属性,用来在拦截方法中真正的去调用服务商提供的功能。
public class ServiceProxy implements InvocationHandler{
    private Service service;
    public ServiceProxy(Service service) {
        this.service = service;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("此处打印日志。。。。。");
        return method.invoke(service, args);
    }
}
  • 其次,我们该如何切入到原来的调用链条中呢?在java语言中都是对象调用方法,原来的中间商已经没有修改的余地了。所以,我们不得不改造中间商这个对象。为了保证原来中间商调用服务商的方法的逻辑不变。中间商就必须实现服务商同样的接口才行,或者继承服务商。然后,中间商要做的事情就是:进入一个统一的入口:拦截器,然后再由拦截器分发调用请求。到这里,中间商的作用已经高度抽象化了。
    唯一需要变化的就是中间商需要实现我们的自定义接口,或者继承服务商,这是为了客户方便调用。否则在客户调用的过程中,是没办法调用到具体的方法的。这里,jdk选用了实现接口的方案,也是符合面向接口的编程思想。当然,也是可以继承服务商,只是jdk没有这么去做而已,并不是什么好多人说的java是单继承多实现这个原因造成的。这不过是不同的方案选择而已。说到后面,大家应该会更深刻的理解。
    高度抽象化了的中间商的结构是这样的:继承服务商或者实现接口,每个方法中,都是先调用我们的拦截器,然后调用服务商。高度抽象的中间商自然就不用我们去开发了,jdk就帮我们代劳了。就像下面这样:
Service serviceProvider = new ServiceProvider();
ServiceProxy serviceProxy = new ServiceProxy(serviceProvider);
//jdk的动态代理,实现动态创建代理类。
Object proxyObj = Proxy.newProxyInstance(
                serviceProvider.getClass().getClassLoader(),
                serviceProvider.getClass().getInterfaces(), 
                serviceProxy
                );
Service proxy = (Service)proxyObj;
proxy.query();

jdk通过我们传入的接口拦截器 这两个参数,就动态创建出来了我们需要的中间商。与静态代理相比。我们少了自己创建中间商这一步,这还不是最关键的。这只是我们解决问题路上的一个附带结果,我们不是真的为了省略中间商而来,回头看,我们为啥要改造静态代理。我们需要切入一个实现通用业务逻辑的拦截器,然而这个拦截器的切入又需要改造中间商。改造之后的中间商又是高度抽象化的,可以由jdk来代劳。
所以,最终,我们解决了我们的问题,也砍掉了中间商
我想再说一下,jdk生成的动态代理类,这个类,如果我们去看jdk源码的话,底层是要用到字节码的技术,动态的先按照固定模板拼凑出来实现了接口的代理类,然后,编译为class文件,加载到内存中。了解到这一步,我们自然而然的就想到了,我们可以动态的拼凑一个继承了服务商的代理类。这里根本就没有要让服务商去继承的任何动机,就不会违反java单继承的原则。所以,我才说,jdk动态代理实现必须拥有接口,是方案选择的不同而已,是面向接口编程思想的体现而已。根本不是某些人说的java是单继承多实现造成的。cglib库的存在也很好的说明了这个问题。

总结

上面的代理模式,我们作为开发者需要做什么工作,我们处于什么位置。如果在学习设计模式的过程中,我们很容易昏了头。往往感觉很多设计模式都是雷同的。
所以,我慢慢感觉到,在学习涉及模式的时候,我们在利用这个设计模式的时候,是处于怎样的一个位置,我们需要做哪些开发工作。
就拿代理模式来说

  • 在日常的使用中,服务商是现成的。我们需要利用动态代理需要实现拦截器,需要在客户调用的地方,改造代码。
  • 而在Spring框架中,我们是配置AOP注解,实现我们的拦截器的功能。但是,我们不需要改造客户调用的代码,Spring帮我们完成了。所以,Spring是帮我们封装了。我们明明是在使用动态代理,但是确实无感的。
    你看,同一个设计模式,我们所处的位置不同,需要做的开发工作也是不同的,感受也是不同的。
    我们在学习涉及模式的时候,不要总认为,我们就是那个client。其实不是这样的,我们在这个设计模式中的角色是不固定的。另外,client也不是最关键的。设计模式的核心思想就是以不变应万变。我们要关注的是那些要时常变化的部分。有些场景,client变化很重要也很频繁,所以会形成一个模式;有些场景,我们根本不关心client,关心的是,调用链条中间的那一部分,所以会形成一个模式;有些场景,我们关心的是最终的结果部分,所以会形成一个模式。
    这整个调用链条的业务没有变,所以很多设计模式看起来很相似。但是,我们关心的,或者说时常变化的节点不同,所以说每个设计模式都不同。

相关文章

  • 代理模式的理解,以及由此引发对设计模式的理解

    在看狂神讲Spring AOP的时候,又顺便讲到了代理模式。 静态代理 静态代理模式还是比较好理解的。就是在客户和...

  • 代理模式和动态代理实战应用

    代理设计模式 java有20多种设计模式,代理模式肯定是非常出名的一种。代理模式可以理解为不直接访问对象,让代理对...

  • Typescript 代理模式(Proxy)

    标签: 前端 设计模式 代理模式 typescript proxy 请仔细阅读下面代码,理解其中的设计理念。 代理...

  • 代理模式详解:静态代理+JDK/CGLIB 动态代理实战

    1. 代理模式 代理模式是一种比较好的理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real o...

  • 6. iOS面试题其他篇1

    常用的设计模式 单例模式 组合模式 观察者模式 代理模式 享元模式 工厂方法模式 抽象工厂模式 MVC的理解 数据...

  • Proxy代理者模式(一)

    摘要 本篇笔记针对Java设计模式中最难理解的代理者模式进行讲解,从静态代理、动态代理,及Java相关代理类的应用...

  • 静态代理与动态代理

    简介 看了挺多关于代理方面的文章,这是我个人对代理的一些粗浅的理解。 代理模式是一种毕竟常见的设计模式。就好比歌手...

  • 我对代理模式的理解

    我在学校迎新晚会上见到一个跳舞的女生,第一眼就想把她占为己有,可是我与她素不相识,只能找其他同学偷偷要来她的联系方...

  • 设计模式 ~ 代理设计模式

    代理设计模式是结构性设计模式的一种,你可以这么理解代理,当你需要在家撸代码后提交到 Gitlab 上,但是发现由于...

  • 原型和原型链

    一、理解原型设计模式以及 JavaScript 中的原型规则 设计模式 工厂模式 在函数内创建一个对象,给对象赋予...

网友评论

      本文标题:代理模式的理解,以及由此引发对设计模式的理解

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