5代理模式

在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能
一般情况下,让代理类和原始类实现同样的接口。
如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。可以让代理类继承原始类的方法来实现代理模式。

原理解析

给controller加入性能计数器功能

普通写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserController {
//...省略其他属性和方法...
private MetricsCollector metricsCollector; // 依赖注入

public UserVo login(String telephone, String password) {

long startTimestamp = System.currentTimeMillis();
// login逻辑
long endTimeStamp = System.currentTimeMillis();

RequestInfo requestInfo = new RequestInfo("login", endTimeStamp - startTimestamp, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return UserVo;
}
}

代理模式-接口实现

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 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) {
//...
}

}

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();
RequestInfo requestInfo = new RequestInfo("login", endTimeStamp - startTimestamp, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}

}

//UserControllerProxy使用举例
//因为原始类和代理类实现相同的接口,是基于接口而非实现编程
//将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
IUserController userController = new UserControllerProxy(new UserController());

代理模式-继承实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
RequestInfo requestInfo = new RequestInfo("login", endTimeStamp - startTimestamp, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return userVo;
}
}
//UserControllerProxy使用举例
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
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();

String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
RequestInfo requestInfo = new RequestInfo(apiName, endTimeStamp - startTimestamp, startTimestamp);
metricsCollector.recordRequest(requestInfo);
return result;
}
}
}

//应用
MetricsCollectorProxy proxy = new MetricsCollectorProxy();
IUserController userController = (IUserController) proxy.createProxy(new UserController());

应用

代理模式在RPC、缓存中的应用

其他应用:监控、统计、鉴权、限流、事务、幂等、日志。

缓存

需求:开发一个有缓存功能接口,对于某些接口请求,如果入参相同,在设定的过期时间内,直接返回缓存结果