1 静态代理
在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
举个栗子🌰 监控登录注册接口调用时长。
MetricsCollector 类用来收集接口请求的原始数据,比如访问时间、处理时长等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class UserController { private MetricsCollector metricsCollector;
public UserVo login(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
}
public UserVo register(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
} }
|
很明显,上面的写法有两个问题。
- 性能计数器框架代码侵入到业务代码中,跟业务代码高度耦合。如果未来需要替换这个框架,那替换的成本会比较大。
- 收集接口请求的代码跟业务代码无关,本就不应该放到一个类中。业务类最好职责更加单一,只聚焦业务处理。
为了将框架代码和业务代码解耦,代理模式就派上用场了。
代理类 UserControllerProxy 和原始类 UserController 实现相同的接口 IUserController。UserController 类只负责业务功能。代理类 UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public interface IUserController { UserVo login(String telephone, String password); UserVo register(String telephone, String password); }
public class UserController implements IUserController {
@Override public UserVo login(String telephone, String password) { }
@Override public UserVo register(String telephone, String password) { } }
public class UserControllerProxy implements IUserController { private MetricsCollector metricsCollector; private UserController userController;
public UserControllerProxy(UserController userController) { this.userController = userController; this.metricsCollector = new MetricsCollector(); }
@Override public UserVo login(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
UserVo userVo = userController.login(telephone, password);
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
return userVo; }
@Override public UserVo register(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
UserVo userVo = userController.register(telephone, password);
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
return userVo; } }
IUserController userController = new UserControllerProxy(new UserController());
|
参照基于接口而非实现编程的设计思想,将原始类对象替换为代理类对象的时候,为了让代码改动尽量少,在刚刚的代理模式的代码实现中,代理类和原始类需要实现相同的接口。
但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(比如它来自一个第三方的类库),我们也没办法直接修改原始类,给它重新定义一个接口。在这种情况下,我们该如何实现代理模式呢?
对于这种外部类的扩展,我们一般都是采用继承的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class UserControllerProxy extends UserController { private MetricsCollector metricsCollector;
public UserControllerProxy() { this.metricsCollector = new MetricsCollector(); }
public UserVo login(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
UserVo userVo = super.login(telephone, password);
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
return userVo; }
public UserVo register(String telephone, String password) { long startTimestamp = System.currentTimeMillis();
UserVo userVo = super.register(telephone, password);
long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; RequestInfo requestInfo = new RequestInfo("register", responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo);
return userVo; } }
UserController userController = new UserControllerProxy();
|
2 动态代理
可以发现,静态代理完美的分离了收集代码与请求业务代码,但是,如果我们需要收集的类过多时,每个类都要写一个代理类,这会导致项目中类的个数成倍增加,增加了代码维护成本。并且,每个代理类中的代码都有点像模板式的 “重复” 代码,也增加了不必要的开发成本。
现在就是动态代理的表现的时候,就是我们不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
动态代理底层依赖的就是 Java 的反射语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class MetricsCollectorProxy { private MetricsCollector metricsCollector;
public MetricsCollectorProxy() { this.metricsCollector = new MetricsCollector(); }
public Object createProxy(Object proxiedObject) { Class<?>[] interfaces = proxiedObject.getClass().getInterfaces(); DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject); return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler); }
private class DynamicProxyHandler implements InvocationHandler { private Object proxiedObject;
public DynamicProxyHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTimestamp = System.currentTimeMillis(); Object result = method.invoke(proxiedObject, args); long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp - startTimestamp; String apiName = proxiedObject.getClass().getName() + ":" + method.getName(); RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo); return result; } } }
MetricsCollectorProxy proxy = new MetricsCollectorProxy(); IUserController userController = (IUserController) proxy.createProxy(new UserController());
|
android-aspectjx AOP 底层的实现原理就是基于动态代理。
3 Android 源码中的代理模式实现
彻底理解Android Binder通信架构
Binder系列8—如何使用Binder
Binder系列9—如何使用AIDL
4 总结
代理模式最常用的一个应用场景就是,在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。实际上,前面举的搜集接口请求信息的例子,就是这个应用场景的一个典型例子。
感谢
设计模式之美
《Android 源码设计模式解析与实战》
以及上文中的链接