结构型-Proxy

简介: 代理模式(Proxy Design Pattern)的原理和代码实现都不难掌握。它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。1. 代理模式的原理与实现 在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。

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


  1. 代理模式的原理与实现


     在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。


  1. 动态代理的原理与实现


     静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。


  1. 代理模式的应用场景


    代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在 RPC、缓存等应用场景


什么时候使用接口,什么时候使用继承


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


普通代理


直接上代码

interface IUserController

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


class UserController

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

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

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 调用类

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




目录
相关文章
|
7月前
|
设计模式 C#
设计模式之代理模式(Proxy)
设计模式之代理模式(Proxy)
|
设计模式 安全 Java
设计模式~代理模式(Proxy)-17
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 【跟MM在网上聊天,一开头总是“hi, 你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这
66 0
|
设计模式 Java
设计模式2 - 代理模式【Proxy Pattern】
设计模式2 - 代理模式【Proxy Pattern】
51 0
|
设计模式 SQL 安全
Java设计模式-代理模式(Proxy)
Java设计模式-代理模式(Proxy)
|
Arthas Java 测试技术
结构型模式 - 代理模式(Proxy Pattern)
结构型模式 - 代理模式(Proxy Pattern)
|
设计模式 Java
设计模式-Proxy代理模式
设计模式-Proxy代理模式
设计模式-Proxy代理模式
|
设计模式 安全 Java
【Java设计模式】代理模式(Proxy Pattern)
为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介作用。
159 0
【Java设计模式】代理模式(Proxy Pattern)
|
设计模式 缓存 Java
浅谈JAVA设计模式之——代理模式(proxy)
为其他对象提供一种代理以控制对这个对象的访问。
135 0
浅谈JAVA设计模式之——代理模式(proxy)
|
监控 数据库
代理模式(Proxy)
转载 https://blog.csdn.net/lovelion/article/details/8228042 某软件公司承接了某信息咨询公司的收费商务信息查询系统的开发任务,该系统的基本需求如下: (1) 在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统; (2) 在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用。
1037 0