在之前,我们学习的都是设计模式中的创建型模式,
单例模式为了创建全局唯一的对象,工厂模式用来创建继承或实现同一接口或者父类的一组对象,建造者模式是通过设置不同的可选参数,来定制化的常见不同类型的对象,原型模式是针对创建成本比较大的对象,利用对已有对象进行复制的方式创建
而现在,是结构型模式的时代了,如何组合类和类,设置一些经典的结构来解决一些特定常见的问题,常见的结构型的模式包括,代理模式 桥接模式 装饰器模式 适配器模式 门面模式 组合模式 享元模式
今天就是代理模式,就是在不改变原有类的基础上,引入代理类来给原始类加功能
例如我们之前开发的统计时间的框架,在进行真正的统计的时候,如果按照之前的方式,这样写
public class UserController {
//…省略其他属性和方法… private MetricsCollector metricsCollector; // 依赖注入 public UserVo login(String telephone, String password) { long startTimestamp = System.currentTimeMillis(); // … 省略login逻辑… long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp – startTimestamp; RequestInfo requestInfo = new RequestInfo(“login”, responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo); //…返回UserVo数据… } public UserVo register(String telephone, String password) { long startTimestamp = System.currentTimeMillis(); // … 省略register逻辑… long endTimeStamp = System.currentTimeMillis(); long responseTime = endTimeStamp – startTimestamp; RequestInfo requestInfo = new RequestInfo(“register”, responseTime, startTimestamp); metricsCollector.recordRequest(requestInfo); //…返回UserVo数据… } } |
这样的使用方式,实际上和业务代码高度耦合了,让我们的计数代码入侵到了业务代码中,为了方便解耦,我们使用了代理类来进行解耦工作,我们在原有类的基础上,创建了代理类UserControllerProxy
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) { //…省略login逻辑… //…返回UserVo数据… } @Override public UserVo register(String telephone, String password) { //…省略register逻辑… //…返回UserVo数据… } } 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; } } |
//UserControllerProxy使用举例
//因为原始类和代理类实现相同的接口,是基于接口而非实现编程
//将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
IUserController userController = new UserControllerProxy(new UserController());
基于接口而非实现的编程思想,我们将原始类替换为了代理类对象,虽然看起来和原有业务代码解耦了,但是这种代码不容易编写,重复代码过多,如果有50个要添加附加功能的原始类,那么就需要创建50个对应的代理类,这就让整个项目类个数成倍增加
于是提供了动态代理这种神器,不用事先的为每一个类编写代理类,而是在运行时候,动态的常见原始类对应的代理类,最后使用的时候将代理类换为原始类
Java中就提供了动态代理,来实现之前说的功能,比如如下的MetricCollectorProxy作为一个代理类,就会动态给每一个需要收集接口的请求信心创建代理类
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使用举例
MetricsCollectorProxy proxy = new MetricsCollectorProxy();
IUserController userController = (IUserController) proxy.createProxy(new UserController());
这就是Java提供的动态代理,实际上,SpringAOP的底层实现,也是基于的代理,有用户配置好需要给哪些类创建代理,并且设置好方法,Spring就会为这些类创建动态代理对象,在JVM中替换原始类
这个常见的业务场景很多
1.非功能性需求的开发
在一些业务系统中开发一些非功能的需求 监控 日志 鉴权 事务等,让代理类来统一的处理,程序员来进行关注业务的开发即可
2.代理类在RPC框架中的实现
客户端在使用RPC服务的时候,就像是使用本地函数,无需使用服务器交互的细节,除此外,RPC服务器的开发者只需要开发业务逻辑,不关注和客户端的交互细节
最后看一个小案例
假设我们需要开发一个接口请求支持缓存的功能,对于某些接口,如果入参相同,则直接返回结果,如果入参不同,则重新进行逻辑计算,但是有些接口需要每次都进行计算逻辑,那可以开发两个接口,一个支持缓存,一个支持实时查询,对于需要实时数据的需求,可以调用实时调用的接口,对于不需要实时数据的需求,调用缓存
那么就可以使用代理类,对于所有支持缓存接口所在的类上,创建新的代理类,请求到来的时候,根据是否需要使用缓存来进行判断从哪拿取数据