在之前,我们学习的都是设计模式中的创建型模式,

单例模式为了创建全局唯一的对象,工厂模式用来创建继承或实现同一接口或者父类的一组对象,建造者模式是通过设置不同的可选参数,来定制化的常见不同类型的对象,原型模式是针对创建成本比较大的对象,利用对已有对象进行复制的方式创建

而现在,是结构型模式的时代了,如何组合类和类,设置一些经典的结构来解决一些特定常见的问题,常见的结构型的模式包括,代理模式 桥接模式 装饰器模式 适配器模式 门面模式 组合模式 享元模式

今天就是代理模式,就是在不改变原有类的基础上,引入代理类来给原始类加功能

例如我们之前开发的统计时间的框架,在进行真正的统计的时候,如果按照之前的方式,这样写

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服务器的开发者只需要开发业务逻辑,不关注和客户端的交互细节

最后看一个小案例

假设我们需要开发一个接口请求支持缓存的功能,对于某些接口,如果入参相同,则直接返回结果,如果入参不同,则重新进行逻辑计算,但是有些接口需要每次都进行计算逻辑,那可以开发两个接口,一个支持缓存,一个支持实时查询,对于需要实时数据的需求,可以调用实时调用的接口,对于不需要实时数据的需求,调用缓存

那么就可以使用代理类,对于所有支持缓存接口所在的类上,创建新的代理类,请求到来的时候,根据是否需要使用缓存来进行判断从哪拿取数据

发表评论

邮箱地址不会被公开。 必填项已用*标注