代理模式实现方式及优缺点对比

https://www.cnblogs.com/zhangxufeng/p/9162182.html

代理模式最典型的应用就是AOP,本文结合主要讲解了代理模式的几种实现方式:静态代理和动态代理,这里动态代理又可以分为jdk代理和Cglib代理,另外,本文也对这几种代理模式的优缺点进行了对比。

代理,顾名思义,即代替被请求者来处理相关事务。代理对象一般会全权代理被请求者的全部只能,客户访问代理对象就像在访问被请求者一样,虽然代理对象最终还是可能会访问被请求者,但是其可以在请求之前或者请求之后进行一些额外的工作,或者说客户的请求不合法,直接拒绝客户的请求。如下图所示为代理模式的一份简图:

代理模式的角色:

  • ISubject:代理者与被代理者共同实现的接口,可以理解为需要代理的行为;
  • SubjectImpl:被代理者,其为具有某种特定行为的实现者;
  • SubjectProxy:代理者,其会全权代理SubjectImpl所具有的功能,在实现其功能的基础上做一些额外的工作;
  • Client:客户端,客户端访问代理者与访问被代理者具有类似的效果,其无法区分访问的是代理者还是被代理者。

1. 静态代理

静态代理模式也即上图中描述的这种模式,从图中可以看出,SubjectProxy保存一个ISubject实例,当客户端调用SubjectProxy的request()方法时,其除了做额外的工作之外,还会调用ISubject实例的request()方法。如下是这三个类的一个简单实现:

public interface ISubject {
  void request();
}
public class SubjectImpl implements ISubject {
  @Override
  public void request() {
    System.out.println("request SubjectImpl.");
  }
}
public class SubjectProxy implements ISubject {
  private ISubject target;

  public SubjectProxy(ISubject target) {
    this.target = target;
  }

  @Override
  public void request() {
    System.out.println("before safety check.");
    target.request();
    System.out.println("after safety check.");
  }
}

可以看到,代理对象在调用被代理对象的方法之前和之后都打印了相关的语句。如下是客户端请求示例:

public class Client {
  @Test
  public void testStaticProxy() {
    ISubject subject = new SubjectImpl();
    ISubject proxy = new SubjectProxy(subject);
    proxy.request();
  }
}

运行上述用例,可得到如下结果:

before safety check.
request SubjectImpl.
after safety check.

从客户端访问方式可以看出,客户端获取的是一个实现ISubject接口的实例,其在调用的request()方法实际上是代理对象的request()方法。这种代理方式称为静态代理,并且这种代理方式也是效率最高的一种方式,因为所有的类都是已经编写完成的,客户端只需要取得代理对象并且执行即可。

静态代理虽然效率较高,但其也有不可避免的缺陷。可以看到,客户端在调用代理对象时,使用的是代理对象和被代理对象都实现的一个接口,我们可以将该接口理解为定义了某一种业务需求的实现规范。如果有另外一份业务需求(如进行数据修改),其与当前需求并行的,没有交集的,但是其在进行正常业务之外所做的安全验证工作与当前需求是一致的。如下是我们进行该数据修改业务的实现代码:

public interface IUpdatable {
  void update();
}
public class UpdatableImpl implements IUpdatable {
  @Override
  public void update() {
    System.out.println("update UpdatableImpl.");
  }
}
public class UpdatableProxy implements IUpdatable {
  private IUpdatable updatable;

  public UpdatableProxy(IUpdatable updatable) {
    this.updatable = updatable;
  }

  @Override
  public void update() {
    System.out.println("pre safety check.");
    updatable.update();
    System.out.println("after safety check.");
  }
}

如下是客户端代码:

public class Client {
  @Test
  public void testStaticProxy() {
    ISubject subject = new SubjectImpl();
    ISubject proxy = new SubjectProxy(subject);
    proxy.request();

    IUpdatable updatable = new UpdatableImpl();
    IUpdatable proxy = new UpdatableProxy(updatable);
    proxy.update();
  }
}

可以看到,要实现相同的对象代理功能(安全验证),静态代理方式需要为每个接口实现一个代理类,而这些代理类中的代码几乎是一致的。这在大型系统中将会产生很大的维护问题。

2. 动态代理

① jdk代理

所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可,如下是实现该接口的一个示例:

public class SafetyInvocationHandler implements InvocationHandler {
  private Object target;

  public SafetyInvocationHandler(Object target) {
    this.target = target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("before safety check.");
    Object result = method.invoke(target, args);
    System.out.println("after safety check.");
    return result;
  }
}

如下是客户端调用方式:

public class Client {
  @Test
  public void testDynamicProxy() {
    ISubject subject = new SubjectImpl();
    ISubject proxySubject = (ISubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{ISubject.class}, new SafetyInvocationHandler(subject));
    proxySubject.request();

    IUpdatable updatable = new UpdatableImpl();
    IUpdatable proxyUpdatable = (IUpdatable) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IUpdatable.class}, new SafetyInvocationHandler(updatable));
    proxyUpdatable.update();
  }
}

可以看到,客户端在调用代理对象时使用的都是同一个SafetyInvocationHandler。这里jdk代理其实在底层利用反射为每个需要代理的对象都创建了一个InvocationHandler实例,在调用目标对象时,其首先会调用代理对象,然后在代理对象的逻辑中请求目标对象。这也就是为什么在代理类中可以保存目标对象实例的原因,比如上述的SafetyInvocationHandler,其声明了一个Object类型的属性用来保存目标对象的实例。

jdk代理解决了静态代理需要为每个业务接口创建一个代理类的问题,虽然使用反射创建代理对象效率比静态代理稍低,但其在现代高速jvm中也是可以接受的,在Spring的AOP代理中默认就是使用的jdk代理实现的。这里jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理。

② Cglib代理

Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象实现某个接口的问题。对于需要代理的类,如果能为其创建一个子类,并且在子类中编写相关的代理逻辑,因为“子类 instanceof 父类”,因而在进行调用时直接调用子类对象的实例,也可以达到代理的效果。Cglib代理的原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行。这也就是Cglib代理为什么不需要为每个被代理类编写代理逻辑的原因。这里需要注意的是,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写。如下是使用Cglib代理的一个示例:

/**
 * 被代理类
 */
public class Suject {
  public void request() {
    System.out.println("update without implement any interface.");
  }
}
/**
 * 代理类
 */
public class SafetyCheckCallback implements MethodInterceptor {
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("before safety check.");
    Object result = methodProxy.invokeSuper(o, objects);
    System.out.println("after safety check.");
    return result;
  }
}

如下是客户端访问方式:

public class Client {
  @Test
  public void testCglibProxy() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Suject.class);
    enhancer.setCallback(new SafetyCheckCallback());
    Suject proxy = (Suject) enhancer.create();
    proxy.request();
  }
}

可以看到,客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中。

3. 总结

本文主要对代理模式的三种实现方式进行了详细讲解,并且比较了各个代理方式的优缺点,Spring主要使用的是动态代理方式实现切面编程的。这里读者可能会有一个疑问,即上述代理代码中,根据实现方式的不同,对客户端代码都有一定的侵入性,比如静态代理客户端需要侵入代理类的实例,jdk代理需要侵入Proxy类,而Cglib代理则需要侵入子类子类对象创建等代码。理论上,客户端只需要获取目标对象,无论是否为代理过的,然后调用其相关方法实现特定功能即可。这其实也是工厂方法的强大之处,因为工厂方法会将对象的创建封装起来,对象的具体创建过程可以根据具体的业务处理即可,客户端只需要依赖工厂类调用相关的方法即可。同样的这也就说明了Spring IoC容器是天然支持AOP代理的原因,因为其将对象的创建过程交由容器进行了。

原文地址:https://www.cnblogs.com/wuer888/p/12152134.html

时间: 2024-08-06 13:29:23

代理模式实现方式及优缺点对比的相关文章

JDK动态代理[1]----代理模式实现方式的概要介绍

日常工作中经常会接触到代理模式,但一直没有对其进行深究.代理模式一直就像一团迷雾一样存在我心里,什么是代理模式?为什么要使用代理?代理模式有哪些实现?它的底层机制是怎样的?这些问题促使着我迫切想要揭开代理模式的神秘面纱. 1. 什么是代理模式? 日常生活中我们经常会碰到代理模式,例如我们找房产中介帮我们介绍房子,找婚姻中介帮我们介绍对象,找保洁帮我们打理房间,找律师帮我们进行诉讼等.我们在无形中运用到了代理模式,却不知道它的存在. 2. 为什么要使用代理? 运用代理可以使我们的生活更加便利,有了

SSH深度历险(十一) AOP原理及相关概念学习+xml配置实例(对比注解方式的优缺点)

接上一篇 SSH深度历险(十) AOP原理及相关概念学习+AspectJ注解方式配置spring AOP,本篇我们主要是来学习使用配置XML实现AOP 本文采用强制的CGLB代理方式 SecurityHandler这个通知类可以换成安全性检测.日志管理等等. <span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18

Android中的系统服务(代理模式)

一,系统启动 Android设备的开机流程总得来分可以分为三部分: 加载引导程序 引导程序bootloader是开机运行的第一个小程序,因此它是针对特定的主板与芯片的.bootloader有很多种,可以使用比较流行的如redboot.uboot.ARMBoot等,也可以开发自己的引导程序,它不是Android操作系统的一部分.引导程序也是OEM厂商或者运营商加锁和限制的地方. 引导程序初始化硬件设备.创建存储器空间的映射等软件运行时所需要的最小环境:加载Linux内核镜像文件(本文只针对Andr

C#设计模式之代理模式(四)

15.7 代理模式效果与适用场景 代理模式是常用的结构型设计模式之一,它为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制.代理模式类型较多,其中远程代理.虚拟代理.保护代理等在软件开发中应用非常广泛. 15.7.1 模式优点 代理模式的共同优点如下: (1) 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度. (2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性. 此外,不同类型的代理模式也具有独特的优点,例

设计模式(结构型)之代理模式(Proxy Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之享元模式(Flyweight Pattern)>http://blog.csdn.net/yanbober/article/details/45477551 概述 代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个

C#设计模式之十二代理模式(Proxy Pattern)【结构型】

原文:C#设计模式之十二代理模式(Proxy Pattern)[结构型] 一.引言 今天我们要讲[结构型]设计模式的第七个模式,也是"结构型"设计模式中的最后一个模式,该模式是[代理模式],英文名称是:Proxy Pattern.还是老套路,先从名字上来看看."代理"可以理解为"代替",代替"主人"做一些事情,为什么需要"代理",是因为某些原因(比如:安全方面的原因),不想让"主人"直接

设计模式完结(12)-- 代理模式

代理模式与装饰模式区别: 装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问.换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息. 因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例.并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器. 我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造

框架 day37 Spring3,AOP,代理模式(动态/CGLIB/工厂bean),传统AOP,AspectJ框架(基于xml/注解),切入点表达式,jdbcTemplate

1     AOP 1.1   什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. * AOP采取横向抽取机制,取代了传统纵向继承体系

设计模式三: 代理模式(Proxy) -- JDK的实现方式

设计模式三: 代理模式(Proxy) -- JDK的实现方式 简介 代理模式属于行为型模式的一种, 控制对其他对象的访问, 起到中介作用. 代理模式核心角色: 真实角色,代理角色; 按实现方式不同分为静态代理和动态代理两种; 意图 控制对其它对象的访问. 类图 实现 JDK自带了Proxy的实现, 下面我们先使用JDK的API来演示代理如何使用, 随后再探究Proxy的实现原理,并自己来实现Proxy. JDK代理类的使用: (InvocationHandler,Proxy) 使用JDK实现的代