顶部左侧内容
百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 在线教程 > 正文

了解代理模式这一篇文章就够了(几种常见的代理模式)

gosiye 2024-08-26 14:23 7 浏览 0 评论

基本介绍

代理模式的核心思想就是:为一个对象(被代理对象)提供一个代理对象,并且通过代理对象控制对原来被代理对象的访问。可以简单理解为通过代理对象访问目标对象。这样做最大的好处就是可以在目标对象实现的基础上,增强额外的功能,起到扩展目标对象的效果。

被代理的对象可以是远程对象、创建时开销大的对象、需要安全控制的对象等。实现代理模式有不同的形式,主要有:静态代理动态代理(也被称为JDK代理或接口代理)、Cglib代理(不需要实现接口,在内存中动态创建代理对象)。

单纯的通过文字描述大家可能对代理模式的理解还是不够深刻,我们可以通过一个简单的UML类图来加深对该模式的理解。

代理模式UML类图:


UML类图讲解:

TargetObject:表示目标对象也就是被代理的对象。
ProxyObject:代表的是代理对象。
Client:代表客户端,使用代理对象的类。

以上就是代理模式的一些基本概念,通过这些介绍相信大家对代理模式已经有了一个简单的认识,接下来就让菜鸟详细介绍一下上面提出的三种实现代理模式的方式,从而更加深入的了解代理模式。

静态代理

使用静态代理时需要让目标对象和代理对象一起实现相同的接口或者继承相同的父类。这样做的目的就是为了通过调用代理对象中和目标对象相同的方法来实现调用目标对象的方法,从而达到代理的效果。

静态代理UML类图:


代码实现:

代理对象和目标对象实现的共同接口

public interface CommonInterface {

  void method();

}

目标对象

public class TargetObject implements CommonInterface {

  @Override
  public void method() {
    System.out.println("被代理的类中的方法执行了!");
  }
}

代理对象

public class ProxyObject implements CommonInterface {

  // 通过接口聚合被代理的类
  private CommonInterface target;

  // 构造器
  public ProxyObject(CommonInterface target) {
    this.target = target;
  }

  @Override
  public void method() {
    System.out.println("开始静态代理!");
    // 执行目标类的方法。
    this.target.method();
    System.out.println("结束静态代理!");
  }
}

客户端测试类

public class Client {

  public static void main(String[] args) {
    TargetObject targetObject = new TargetObject();
    ProxyObject proxyObject = new ProxyObject(targetObject);
    proxyObject.method();
  }
}

执行结果


总结

优点:实现起来简单,并且容易理解,只要确保目标对象和代理对象实现共同的接口或继承相同的父类就可以在不修改目标对象的前提下进行扩展。

缺点:缺点也是显而易见的,必须有共同接口或父类,并且需要为每一个目标类维护一个代理类,当需要代理的类很多时会创建出大量代理类。一旦接口或父类的方法有变动,目标对象和代理对象都需要作出调整。

这种方式虽说简单但是灵活性不足。

动态代理(JDK代理、接口代理)

使用JDK代理的基本前提是目标对象必须要实现接口。使用该方式代理对象是不需要手动编写代理类的,代理类是通过JDK的API动态的在内存中创建的。

动态代理UML类图



代码实现JDK代理

目标对象需要实现的接口

public interface TargetInterface {

  void method();

}

目标对象

public class TargetObject implements TargetInterface {

  @Override
  public void method() {
    System.out.println("被代理的类中的方法执行了!");
  }
}

代理工厂

public class ProxyFactory {

  // 目标对象
  private Object target;

  // 构造器
  public ProxyFactory(Object target) {
    this.target = target;
  }

  // 通过JDK的API在内存中动态生成一个代理对象。
  public Object getProxyInstance() {

    /**
     * 参数介绍
     * ClassLoader loader:目标对象的类加载器
     * Class<?>[] interfaces:目标对象实现的接口
     * InvocationHandler h:事件处理器。执行目标对象的方法时,
     *  会触发该处理器中的invoke方法。
     */
    return Proxy
        .newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
            /**
             * 此处用到了java8的Lambda表达式。看不太懂的有可以用传统方式。
             * 传统方式:new InvocationHandler() { // 重写invoke方法 }
             *
             * 参数说明:
             * Object proxy:代理类的实例
             * Method method:目标对象方法
             * Object[] args:目标对象方法入参
             */
            (proxy, method, args) -> {
              System.out.println("JDK动态代理开始!");
              // 执行目标类的方法。returnVal是方法的返回值,没有返回值时为空。
              Object returnVal = method.invoke(target, args);
              System.out.println("JDK动态代理结束!");
              return returnVal;
            });
  }
}

客户端测试类

public class Client {

  public static void main(String[] args) {
    TargetObject targetObject = new TargetObject();
    ProxyFactory proxyFactory = new ProxyFactory(targetObject);
    // 注意:此处强转的类型只能是目标类实现的接口类型!!!
    TargetInterface proxyInstance = (TargetInterface) proxyFactory.getProxyInstance();
    proxyInstance.method();
  }
}

执行结果


总结

优点:使用JDK动态代理,可以将代理类的创建推迟到运行时期在内存中创建,这样就可以大大减少代理类的创建和维护。

缺点:目标类必须实现接口,还增加了代码的理解难度。

Cglib代理

基本介绍以及注意事项

1、使用Cglib进行动态代理的目标类不需要强制要求实现接口。
2、Cglib代理也叫做子类代理,它是在内存中动态的创建一个目标类的子类对象从而实现对目标类的代理。Cglib代理也是属于动态代理的一种。
3、Cglib是一个强大的高性能代码生成工具,它可以在程序运行时期扩展类与实现接口。许多AOP框架都
使用Cglib。
4、Cglib的底层是通过字节码处理框架ASM来转换字节码并生成新类的。
5、注意:使用Cglib代理的类不能是final修饰的,并且被代理类中final和static修饰的方法不会被代理。

Cglib代理UML类图


示例代码

目标对象

public class TargetObject {

  public void method() {
    System.out.println("被代理的类中的方法执行了!");
  }
}

代理工厂

public class ProxyFactory implements MethodInterceptor {

  // 目标对象
  private Object target;

  // 构造方法
  public ProxyFactory(Object target) {
    this.target = target;
  }

  // 创建目标对象
  public Object getProxyInstance() {
    // 创建一个工具类
    Enhancer enhancer = new Enhancer();
    // 设置父类
    enhancer.setSuperclass(target.getClass());
    // 设置回调函数
    enhancer.setCallback(this);
    // 创建代理对象
    return enhancer.create();
  }

  /**
   * 重写intercept,完成对目标对象中方法的扩展
   *
   * 参数介绍: 
   *    Object o:cglib动态生成的对象。 
   *    Method method:目标类中的方法。
   *    Object[] objects:方法的参数。
   *    MethodProxy methodProxy:根据目标类的方法生成的代理方法。
   */
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
      throws Throwable {
    System.out.println("Cglib代理模式开始!");
    /**
     * invoke方法入参说明:
     *  Object obj:方法所对应的类的对象,在该代码示例中指的就是目标类。
     *  Object... args:方法的入参。
     */
    Object invokeValue = method.invoke(target, objects);
    System.out.println("Cglib代理模式结束!");
    return invokeValue;
  }
}

客户端测试类

public class Client {

  public static void main(String[] args) {
    TargetObject targetObject = new TargetObject();
    ProxyFactory proxyFactory = new ProxyFactory(targetObject);
    TargetObject proxyInstance = (TargetObject) proxyFactory.getProxyInstance();
    // 执行代理对象的方法,会触发ProxyFactory类的intercept方法,从而实现对目标对象的调用。
    proxyInstance.method();
  }
}

执行结果



总结:

优点:不需要额外维护代理类,并且也不需要强制要求被代理的目标对象实现接口,使得代码更加的灵活。

缺点:和JDK的动态代理一样,增加了代码的理解难度。

总结

通过“菜鸟”的介绍相信大家对代理模式也有了一定的认识。以上介绍的三种方式其使用时的选择时机需要根据实际情况考虑,一般当我们需要代理的类少的时候,可以考虑使用静态代理,但是静态代理的选择一般都比较少,因为其维护成本要高,并且局限也大。当被代理的目标类实现了接口时可以考虑使用JDK代理,反之当目标类没有实现接口时就只能选择使用Cglib代理了。

今天的分享就到这里了,如果感觉“菜鸟”写的文章还不错,记得点赞关注呦!你们的支持就是我坚持下去的动力。文章哪里写的有问题的也希望大家可以指出,我会虚心受教。


相关推荐

全球最大的H5网站模板库(h5页面模板下载)

当今社会,互联网迅猛发展,在网络营销中,客户往往通过企业的网站建设留下对该企业的第一印象,一个优秀的企业网站已成为企业发展的重要纽带,嗨创H5,拥有国内外一流的技术团队,潜心专研网站建设6年,是全球最...

wordpress集团公司网站模板:XSgr(wordpress建站公司)

小兽wordpress推出一款高端集团公司主题,打造高品质官网。高端是一种态度和坚持,因为我坚信贴合产品及品牌理念的高端深度定制才能最大化地呈现企业的务实严谨与产品的专业品质相比,某种程度上讲–...

私心推荐,小编酷爱的五款高逼格网站模板

建站宝盒的网站模板上千套之多,各有各的风格色彩,但是,弱水三千,小编我却只取一瓢饮,在这上千套模板之中,小编酷爱的网站模板有五套,让小编私心推荐一下吧!1、茶叶贸易公司网站模板小编对这款网站模板可是一...

「书讯」政府网站用户行为研究与应用

《政府网站用户行为研究与应用》作者:刘合翔著出版日期:2018年6月开本:16开出版社:经济管理出版社小编推荐《政府网站用户行为研究与应用》的主题是关于政府网站用户行为的特征规律及其在政府网站优...

免费服务器-搭建模板网站的操作流程(图文版)

之前发文《创业者的官网:如何搭建免费云服务器及操作面板(图文版)》,因为做了视频才发现,创业者对视频的需求,远远低于对图文解说的需求。因此,补充图文教程,不清楚的看官们,可以直接看视频版本进行细部学...

快收藏这些高逼格H5网站模板吧,不绕弯子直接下载

上面这些响应式H5网站是不是很炫酷,比起那些“在线一键生成”是不是好太多了?关键是,那些一键制作都不会开放源码给你,自定义性也很局限。不过说到底还是难看。今天笔者推荐大家一个模板网站,全都是高质量的响...

如何开发网站建设管理系统模板(如何开发网站建设管理系统模板图片)

根据用户网站需求文档设计美工图,并设计数据库结构,让网站开发人员可以更多地关注前台美工,先对照美工图,编写静态HTML页面,按网站建设管理系统模板语法,修改编写好的静态HTML页面,运行。不再需要对...

C语言的数据类型介绍(c语言的数据类型介绍是什么)

在计算机系统中,数据是放在内存中的,数字、文字、符号、图形、音频、视频等数据都是以二进制形式存储在内存中的,它们并没有本质上的区别,那么0001000该理解为数字8呢,还是图像中某个像素的颜色...

C 语言格式化输出函数中常用的格式符号

在之前介绍输入输出函数的文章中,有提到格式化输入输出函数都有包含一种特殊的符号——格式符号。那篇文章中关于格式符号也只是一笔带过,没有进行深入挖掘。本篇文章主要对输出函数(printf)中的一些常用格...

C#中的类型转换(c#数据转换类)

计算机存储的基本单位:字节我们知道一个字节(Byte)有8个比特(bit)构成,比特是存储的最小单位,表示0和1,但为什么计算机存储的基本单位是字节,而不是比特呢?假设我们要存储数字3(二进制:11...

Java8中String内存空间占用分析(电脑里下载的文件怎样删除才不会占用内存空间)

1.前言分析之前,简单回顾一下对象的内存分布。在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三部分:对象头、实例数据和对齐填充。对象头包含两部分内容:MarkWord和类型指针。实例数据...

「每日C语言」数据类型大小和取值范围

对于c语言来说,数据类型是一个很重要的概念和知识点,它涉及到的是内存的空间,这在和硬件交互的时候是非常重要的。K&R给出了7个数据类型相关的关键字,分别是:int、long、short、uns...

【c语言学习笔记】数据类型(c语言里面的数据类型)

c语言学习笔记,欢迎大家能在评论区提出我学习错误的地方方便我进行改正~在计算机中,计算机用二进制来储存数据,在c语言中有许多的数据类型用来存储数据,当然不同的数据类型所用的内存占用也不一样,下面就来用...

关于MySQL varchar类型最大值,原来一直都理解错了

我是架构精进之路,点击上方“关注”,坚持每天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。写在前面关于MySQLvarchar字段类型的最大值计算,也许我们一直都理解错误了,...

C语言数据类型的转换(c语言数据类型的转换方式)

类型转换在C语言程序中,经常需要对不同类型的数据进行运算,为了解决数据类型不一致的问题,需要对数据的类型进行转换。例如一个浮点数和一个整数相加,必须先将两个数转换成同一类型。C语言程序中的类型...

取消回复欢迎 发表评论: