代理设计模式

代理设计模式


  1. 简单介绍

    代理设计模式是一种结构型设计模式。该模式是将能在不修改既有代码的前提下,完成对被代理对象的功能扩展,以及安全控制等功能。

  2. 使用场景

    • 远程代理:目标类存在于另一个地址空间(可以简单理解为在另一台服务器上),在调用目标类接口方法时,可以通过调用代理类的接口方法,再由代理类与远端目标类进行通信。完成远程方法的透明调用。RPC框架的实现原理便是如此
    • 保护代理:目标对象的调用需要进行安全控制时,此时安全控制可交由代理对象,由代理对象过滤不安全的因素
    • 虚拟代理:将创建开销大的对象,交由创建开销小的代理对象进行代理。将大对象的创建延迟到真正调用它的时候才去创建
  3. 场景举例

    Spring框架的核心功能AOP底层采用的便是动态代理模式。通过用户的配置,在不改变既有接口代码的前提下,完成对目标方法的增强。

  4. UML类图

    代理模式UML类图

  5. 实现方式

    • 静态代理

      代理类(Proxy)由用户自己创建,在编译期确定代理对象,即在程序运行前便生成代理类的字节码文件

    • JDK动态代理

      通过JDK自带的Proxy.newProxyInstance()方法,在运行期间生成目标类的代理对象。与静态代理相似的是,目标类需要实现接口

    • cglib动态代理

      目标类不需要实现接口,cglib思想是通过继承目标类,获取目标对象的元信息,故目标类不能为final类,被代理的方法不能为final方法,因为final类型的类或方法不能被子类所继承

  6. 具体实现

    • 描述

      • 背景:为了保证明星(被代理对象)的人身安全,当高空高度大于1米时,替身(代理对象)代理大碗明星完成部分高危动作高空跳跃
      • Jumpable:公共接口(Subject),拥有高空跳跃的接口方法,该方法需要被代理执行
      • SuperStar:明星(被代理对象,RealSubject)。当高空高度较高时,跳跃动作交由替身(被代理对象)完成
      • Substitute:替身(代理对象,Proxy)。当高空高度较高时,由替身(被代理对象)完成该场景下的代理模式,起到了保护明星(被代理对象)的作用
    • 静态代理

      Jumpable.java

      /**
       * 角色:Subject
       * 代理与被代理对象共同接口
       * 接口方法为高空跳跃,即替身(代理类)需要代理明星(被代理类)完成的动作(方法)
       */
      public interface Jumpable {
      
          /**
           * 高空跳下
           */
          void highJump();
      }
      

      SuperStar.java

      /**
       * 角色:RealSubject(目标类)
       * 明星,即目标类
       */  
      public class SuperStar implements Jumpable {
      
          @Override
          public void highJump() {
              System.out.println("明星完成了高空跳跃动作");
          }
      }
      

      Substitute.java

      /**
       * 角色:Proxy
       * 替身,即代理对象
       * 高空较高时,跳跃由替身(代理对象)完成
       * 起到了保护明星(目标对象)的作用
       */
      public class Substitute implements Jumpable {
      
          /**
           * 被代理对象(明星)
           */
          private SuperStar superStar;
      
          /**
           * 高空跳跃的高度
           */
          private double highAltitude;
      
          /**
           * 通过构造方法,将被代理对象聚合到代理对象中
           */
          public Substitute(SuperStar superStar, double highAltitude) {
              this.superStar = superStar;
              this.highAltitude = highAltitude;
          }
      
          /**
           * 高空跳跃
           * 为了保护明星,当高空的高度大于1米,则跳跃动作有替身完成
           * 如果高空的高度小于等于1米,则跳跃动作有替身完成
           */
          @Override
          public void highJump() {
              // 高空高度小于1米
              if (highAltitude <= 1) {
                  System.out.print(String.format("高空为:%s米,", highAltitude));
                  superStar.highJump();
              } else {
                  System.out.print(String.format("高空为:%s米,", highAltitude));
                  System.out.println("为保证明星安全,替身完成了高空跳跃动作");
              }
          }
      }
      

      Client.java

      /**
       * 客户类
       * 可以将该客户类理解为导演
       */  
      public class Client {
      
          public static void main(String[] args) {
      
              // 当高空高度为0.8米时,代理对象将高空跳跃动作交由明星(目标对象)完成
              Jumpable proxy01 = new Substitute(new SuperStar(), 0.8D);
              proxy01.highJump();
      
              // 当高空高度为1.8米时,为保证明星(目标对象)人身安全,高空跳跃动作由替身(代理对象)完成
              Jumpable proxy02 = new Substitute(new SuperStar(), 1.8D);
              proxy02.highJump();
          }
      }
      
    • JDK动态代理

      PowerSupply.java

      /**
       * 角色:Subject
       * 代理与被代理对象共同接口
       * 接口方法为高空跳跃,即替身(代理类)需要代理明星(被代理类)完成的动作(方法)
       */
      public interface Jumpable {
      
          /**
           * 高空跳下
           */
          void highJump();
      }
      

      SuperStar.java

      /**
       * 角色:RealSubject(被代理类)
       *
       */  
      public class SuperStar implements Jumpable {
      
          @Override
          public void highJump() {
              System.out.println("明星完成了高空跳跃动作");
          }
      }
      

      Client.java

      /**
       * 客户类
       * 可以将该客户类理解为导演
       */  
      public class Client {
      
          public static void main(String[] args) {
      
              Jumpable superStar = new SuperStar();
      
              // 手动输入高空高度
              double highAltitude = new Scanner(System.in).nextDouble();
      
              // 通过JDK动态代理,生成明星(目标对象)的代理对象替身substitute
              Jumpable substitute = (Jumpable) Proxy.newProxyInstance(superStar.getClass().getClassLoader(),
                      superStar.getClass().getInterfaces(), (proxy, method, args1) -> {
      
                  // 高空高度小于1米
                  if (highAltitude <= 1) {
                      System.out.printf("高空为:%.2f米,", highAltitude);
                      method.invoke(superStar, args1);
                  } else {
                      System.out.print(String.format("高空为:%.2f米,", highAltitude));
                      System.out.println("为保证明星安全,替身完成了高空跳跃动作");
                  }
      
                  return null;
              });
      
              // 调用代理对象的highJump()方法
              // 输入1.5,输出 —— 高空为:1.50米,为保证明星安全,替身完成了高空跳跃动作
              // 输入0.8,输出 —— 高空为:0.80米,明星完成了高空跳跃动作
              substitute.highJump();
          }
      }
      
    • cglib动态代理

      SuperStar.java

      /**
       * 角色:RealSubject(目标类)
       * 明星,即目标类
       */
      public class SuperStar implements Jumpable {
      
          @Override
          public void highJump() {
              System.out.println("明星完成了高空跳跃动作");
          }
      }
      

      ProxyFactory.java

      /**
       * 代理类工厂,用于创建目标对象代理类,
       * 需要实现MethodInterceptor接口,用于拦截目标类的方法
       */
      public class ProxyFactory implements MethodInterceptor {
      
          /**
           * 被代理对象(明星)
           */
          private SuperStar superStar;
      
          /**
           * 高空跳跃的高度
           */
          private double highAltitude;
      
          /**
           * 通过构造方法,将被代理对象聚合到代理对象中
           */
          public ProxyFactory(SuperStar superStar, double highAltitude) {
              this.superStar = superStar;
              this.highAltitude = highAltitude;
          }
      
          /**
           * 目标对象方法执行时,被拦截进入该方法中
           */
          @Override
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      
              // 高空高度小于1米
              if (highAltitude <= 1) {
                  System.out.print(String.format("高空为:%s米,", highAltitude));
                  superStar.highJump();
              } else {
                  System.out.print(String.format("高空为:%s米,", highAltitude));
                  System.out.println("为保证明星安全,替身完成了高空跳跃动作");
              }
      
              // 无返回值时,返回null即可
              return null;
          }
      
          /**
           * 获取代理对象
           */
          public SuperStar getProxyInstance() {
      
              // 工具类创建
              Enhancer enhancer = new Enhancer();
              // 设备目标对象为代理对象父类
              enhancer.setSuperclass(superStar.getClass());
              // 设置回调对象实例
              // ProxyFactory实现了MethodInterceptor接口,故设置本实例即可
              enhancer.setCallback(this);
              // 创建并返回代理对象
              return (SuperStar) enhancer.create();
          }
      }
      

      Client.java

      /**
       * 客户类
       * 可以将该客户类理解为导演
       */  
      public class Client {
      
          public static void main(String[] args) {
      
              // 1.1 创建目标类
              SuperStar superStar = new SuperStar();
      
              // 1.2 创建代理对象1,设置高空高度为1.8m
              SuperStar substitute1 = new ProxyFactory(superStar, 1.8D).getProxyInstance();
              // 1.2 跳跃动作交由代理类(替身)
              // 输出:高空为:1.8米,为保证明星安全,替身完成了高空跳跃动作
              substitute1.highJump();
      
      
              // 2.1 创建代理对象2,设置高空高度为0.8m
              SuperStar substitute2 = new ProxyFactory(superStar, 0.8D).getProxyInstance();
              // 2.2 跳跃动作交由代理类(替身)
              // 输出:高空为:0.8米,明星完成了高空跳跃动作
              substitute2.highJump();
          }
      }
      
  7. 源码展示

    • MaBatis中的SQL执行器Executor,采用了代理模式,其中接口类为 Executor,目标类为 BaseExecutor 的具体子类( BaseExecutor、SimpleExecutor等),代理类为CachingExecutor。CachingExecutor作为代理类,采用缓存的方式,来提升目标类的查询效率。当内存中存在SQL执行的结果时,则直接将内存中的数据返回,否则调用具体的目标类,执行相关SQL。

      Executor.java

      public interface Executor {
            
          /**
           * 查询操作
           */
          <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
      
          // 剩余代码略
      }
      

      BaseExecutor.java

      public abstract class BaseExecutor implements Executor {
          
          /**
           * 目标类实现的查询操作
           */
          public <E> List<E> query(MappedStatement ms, Object parameter, 
                                   RowBounds rowBounds, ResultHandler resultHandler, 
                                   CacheKey key, BoundSql boundSql) throws SQLException {
              ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
              if (this.closed) {
                  throw new ExecutorException("Executor was closed.");
              } else {
                  if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                      this.clearLocalCache();
                  }
      
                  List list;
                  try {
                      ++this.queryStack;
                      list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                      if (list != null) {
                          this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                      } else {
                          list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                      }
                  } finally {
                      --this.queryStack;
                  }
      
                  if (this.queryStack == 0) {
                      Iterator i$ = this.deferredLoads.iterator();
      
                      while(i$.hasNext()) {
                          BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                          deferredLoad.load();
                      }
      
                      this.deferredLoads.clear();
                      if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                          this.clearLocalCache();
                      }
                  }
      
                  return list;
              }
          }
      }
      

      CachingExecutor.java

      public class CachingExecutor implements Executor {
          
          /**
           * 持有目标类引用
           */
          private Executor delegate;
      
          /**
           * 通过构造函数,聚合目标类
           */
          public CachingExecutor(Executor delegate) {
              this.delegate = delegate;
              delegate.setExecutorWrapper(this);
          }
      
      	/**
           * 对目标类的query方法进行增强
           */
          public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
              
              // 先查缓存,缓存中有结果则直接返回
              Cache cache = ms.getCache();
              if (cache != null) {
                  this.flushCacheIfRequired(ms);
                  if (ms.isUseCache() && resultHandler == null) {
                      this.ensureNoOutParams(ms, parameterObject, boundSql);
                      List<E> list = (List)this.tcm.getObject(cache, key);
                      if (list == null) {
                          list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                          this.tcm.putObject(cache, key, list);
                      }
      
                      return list;
                  }
              }
      
              // 缓存中没有结果,则调用目标类的query()方法
              return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          }      
      }
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值