代理模式在不改变原始类 (或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
为了将框架代码和业务代码解耦,代理模式就派上用场了。代理类 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
| 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, startTimes); 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, startTimes); 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
| 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, startTimes); 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, startTimes); metricsCollector.recordRequest(requestInfo); return userVo; } }
UserController userController = new 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
| 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());
|
Spring AOP 底层的实现原理就是基于动态代理。
应用场景
1. 业务系统的非功能性需求开发
在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理。
2. 代理模式在 RPC、缓存中的应用
实际上,RPC 框架也可以看作一种代理模式,GoF 的《设计模式》一书中把它称作远程代理。通过远程代理,将网络通信、数据编解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解跟服务器交互的细节。除此之外,RPC 服务的开发者也只需要开发业务逻辑,就像开发本地使用的函数一样,不需要关注跟客户端的交互细节。
参考
《设计模式之美》