前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >结构型-Proxy

结构型-Proxy

作者头像
acc8226
发布2022-05-17 15:58:00
3360
发布2022-05-17 15:58:00
举报
文章被收录于专栏:叽叽西

代理模式(Proxy Design Pattern)的原理和代码实现都不难掌握。它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。

  1. 代理模式的原理与实现 在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。
  2. 动态代理的原理与实现 静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
  3. 代理模式的应用场景 代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在 RPC、缓存等应用场景

什么时候使用接口,什么时候使用继承 参照基于接口而非实现编程的设计思想,将原始类对象替换为代理类对象的时候,为了让代码改动尽量少,在刚刚的代理模式的代码实现中,代理类和原始类需要实现相同的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(比如它来自一个第三方的类库),我们也没办法直接修改原始类,给它重新定义一个接口。在这种情况下,对于这种外部类的扩展,我们一般都是采用继承的方式。这里也不例外。我们让代理类继承原始类,然后扩展附加功能。

普通代理

直接上代码

interface IUserController

代码语言:javascript
复制
package proxy;


public interface IUserController {
  void login(String telephone, String password);
  void register(String telephone, String password);
}

class UserController

代码语言:javascript
复制
package proxy;

public class UserController implements IUserController {

  public void login(String telephone, String password) {
    // ... 省略login逻辑...
      System.out.println("login ...");
  }

  public void register(String telephone, String password) {
    // ... 省略register逻辑...
      System.out.println("register ...");
  }
  
}

class UserControllerProxy

代码语言:javascript
复制
package proxy;

import java.time.Instant;

public class UserControllerProxy implements IUserController {
    
  private IUserController userController;

  public UserControllerProxy(IUserController userController) {
    this.userController = userController;
  }

  @Override
  public void login(String telephone, String password) {
    System.out.println("-----使用了普通代理-----");

    long startTimestamp = Instant.now().toEpochMilli();

    // 委托
    userController.login(telephone, password);

    long endTimeStamp = Instant.now().toEpochMilli();
    long responseTime = endTimeStamp - startTimestamp;
    System.out.println(responseTime);
  }

  @Override
  public void register(String telephone, String password) {
    System.out.println("-----使用了普通代理-----");
    long startTimestamp = Instant.now().toEpochMilli();

    userController.register(telephone, password);

    long endTimeStamp = Instant.now().toEpochMilli();
    long responseTime = endTimeStamp - startTimestamp;
    System.out.println(responseTime);
  }
}

动态代理

所谓动态代理(Dynamic Proxy),就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。那如何实现动态代理呢?

示例代码

class UserControllerInvocationHandler

代码语言:javascript
复制
package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.time.Instant;

public class UserControllerInvocationHandler implements InvocationHandler {
    
    private Object proxiedObject;

    public UserControllerInvocationHandler(Object proxiedObject) {
        this.proxiedObject = proxiedObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----使用了动态代理-----");
        Object invoke;
        String methodName = method.getName();
        if (methodName.equals("login") || methodName.equals("register")) {
            long startTimestamp = Instant.now().toEpochMilli();
            invoke = method.invoke(this.proxiedObject, args);
            long endTimeStamp = Instant.now().toEpochMilli();
            long responseTime = endTimeStamp - startTimestamp;
            System.out.println(responseTime);
        } else {
            invoke = method.invoke(this.proxiedObject, args);
        }
        return invoke;
    }

}

Main 调用类

代码语言:javascript
复制
package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main {
    
    public static void main(String[] args) {
        IUserController controller = new UserController();      
        // 静态代理
        IUserController controllerProxy = new UserControllerProxy(new UserController());
        controllerProxy.register("lisi", "666666");
        
        // 动态代理
        ClassLoader loader = controller.getClass().getClassLoader();
        Class<?>[] interfaces = controller.getClass().getInterfaces();
        InvocationHandler handler = new UserControllerInvocationHandler(controller);
        IUserController proxy = (IUserController) Proxy.newProxyInstance(loader, interfaces, handler);
        proxy.register("wangwu", "111");
    }

}

更多

Proxy 只能对 interface 进行代理,无法实现对 class 的动态代理。观察动态生成的代理继承关系图可知原因,他们已经有一个固定的父类叫做 Proxy,Java语法限定其不能再继承其他的父类。要实现对 class 的动态代理,可以使用 CGLIB。

CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于 AOP 框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate 作为一个比较受欢迎的ORM框架,同样使用 CGLIB 来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB 作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

相关资料

参考

设计模式之美设计模式代码重构-极客时间 https://time.geekbang.org/column/intro/250

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 普通代理
  • 动态代理
  • 更多
  • 参考
相关产品与服务
代码托管
CODING 代码托管(CODING Code Repositories,CODING-CR)是为开发者打造的云端便捷代码管理工具,旨在为更多的开发者带去便捷、高效的开发体验,全面支持 Git/SVN 代码托管,包括代码评审、分支管理、超大仓库等功能。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档