代理模式

前言

作为结构型设计模式的一种,代理模式解决的是关于扩展与原有业务不太相关的功能的问题。当我们需要对一些功能进行统一增强(一些公关的功能),但是又不侵入原有代码的时候,代理模式就能发挥大作用。

例如我们熟知的日志功能,关于Spring AOP 的前置,环绕,后置通知等等。大多是基于代理模式来实现的,所谓代理就是代替被代理人(对象)来执行一些事情。

最简单的实现,即在代理类中注入被代理的对象,实现与代理对象同样的接口,在实现方法当中增加目标逻辑,再调用被代理类的目标方法。使用时,我们直接使用代理对象代替目标对象来完成原有的逻辑处理。

动态代理

简单的代理模式虽然解决了代码侵入的问题,但是因为设计模式的引入使得复杂度也有所上升。因为在代理类中我们又把接口所有的方法又重新实现了一遍,基本上没有太大的变动,这造成了很多重复的工作。另外假如我们要增强的功能不止一个的话,我们就需要创建多个代理类来达到目的了。这样的话类结构就会膨胀很多。

为了解决以上的问题,我们引入动态代理的概念来解决这样的问题。即,我们不在事先不需要为每个目标类编写代理类。而是在程序运行时才动态的去组装代理类,然后替换原始类。整个过程基于Java的反射机制来完成。在Java中常使用原始的JDK动态代理和第三方的CGlib动态代理来实现上述过程。

1
2
public class MetricsCollectorProxy {
3
  private MetricsCollector metricsCollector;
4
5
  public MetricsCollectorProxy() {
6
    this.metricsCollector = new MetricsCollector();
7
  }
8
9
  public Object createProxy(Object proxiedObject) {
10
    Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
11
    DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
12
    return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
13
  }
14
15
  private class DynamicProxyHandler implements InvocationHandler {
16
    private Object proxiedObject;
17
18
    public DynamicProxyHandler(Object proxiedObject) {
19
      this.proxiedObject = proxiedObject;
20
    }
21
22
    @Override
23
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24
      long startTimestamp = System.currentTimeMillis();
25
      Object result = method.invoke(proxiedObject, args);
26
      long endTimeStamp = System.currentTimeMillis();
27
      long responseTime = endTimeStamp - startTimestamp;
28
      String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
29
      RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp);
30
      metricsCollector.recordRequest(requestInfo);
31
      return result;
32
    }
33
  }
34
}
35
36
//MetricsCollectorProxy使用举例
37
MetricsCollectorProxy proxy = new MetricsCollectorProxy();
38
IUserController userController = (IUserController) proxy.createProxy(new UserController());

总结

代理模式最常用的应用场景就是在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让目标类只需要关注业务方面的逻辑即可。