05-代理模式
05-代理模式
01.基于接口实现代理模式
- 性能统计代码和业务代码在一起存在的问题。
/**
* 如下在登录代码中收集性能数据存在两个问题:
* 1 性能统计代码侵入业务代码。
* 2 性能统计的代码与业务代码无关,业务代码最好只聚焦业务处理。
*/
public class UserController implements IUserController {
@Override
public boolean login(String phone, String password) {
long start = System.currentTimeMillis();
// 登录的业务操作
long end = System.currentTimeMillis();
System.out.println("登录用时 " + (end - start));
return false;
}
}
- 基础接口实现代理模式。
// 接口
public interface IUserController {
boolean login(String phone, String password);
}
// 真正处理业务的类
public class UserController implements IUserController {
@Override
public boolean login(String phone, String password) {
// 登录的业务操作
return false;
}
}
// 代理类
public class UserControllerProxy implements IUserController {
private final IUserController userController;
public UserControllerProxy(IUserController userController) {
this.userController = userController;
}
@Override
public boolean login(String phone, String password) {
long start = System.currentTimeMillis();
boolean login = userController.login(phone, password);
long end = System.currentTimeMillis();
System.out.println("登录执行时间 " + (end - start));
return login;
}
}
// 代理类使用方式。
UserControllerProxy proxy = new UserControllerProxy(new UserController());
02.基于继承实现代理模式
-
当原始类没有定义接口,并且原始类来自第三方类库,即不能给原始类添加接口,那么就需要使用继承的方式实现代理模式。
-
继承实现代理模式。
public class UserController {
public boolean login(String phone, String password) {
// 登录的业务操作
return false;
}
}
public class UserControllerProxy extends UserController {
@Override
public boolean login(String phone, String password) {
long start = System.currentTimeMillis();
boolean login = super.login(phone, password);
long end = System.currentTimeMillis();
System.out.println("登录时间 " + (end - start));
return login;
}
}
UserControllerProxy proxy = new UserControllerProxy();
03.基于反射实现代理模式
- 基于接口实现代理模式和基于继承实现代理模式也叫作静态代理;基于反射实现代理模式也成为动态代理。
- 对于添加性能统计的功能,静态代理需要将所有的代码重新实现一遍,如果有较多的方法都需要添加附加功能时,就会产生过去的重复代码,导致代码难以维护。
- 基于反射实现代理模式。
public interface IUserController {
boolean login(String phone, String password);
}
public class UserController implements IUserController {
@Override
public boolean login(String phone, String password) {
// 登录的业务操作
return false;
}
}
public class MetricsControllerProxy {
private Object createProxy(Object proxyObject) {
Class<?>[] interfaces = proxyObject.getClass().getInterfaces();
DynamicProxyHandler handler = new DynamicProxyHandler(proxyObject);
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), interfaces, handler);
}
private static class DynamicProxyHandler implements InvocationHandler {
private final Object proxyObject;
public DynamicProxyHandler(Object proxyObject) {
this.proxyObject = proxyObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(proxyObject, args);
long end = System.currentTimeMillis();
System.out.println("执行时间 " + (end - start));
return result;
}
}
}
// 1 代理类会对UserController中的所有的方法添加性能统计功能。
// 2 如果其他的类也需要增加性能统计功能,只需要通过proxy.createProxy(xxx)代理即可。
MetricsControllerProxy proxy = new MetricsControllerProxy();
IUserController userController = (IUserController) proxy.createProxy(new UserController());
userController.login("phone", "password");
04.代理模式总结
- 代理模式的描述:在不改变原始类的情况下,通过代理类来给原始类附加不相关的其他功能。
- 对于静态代理,一般使用基于接口的静态代理。当原始类没有定义接口并无法直接为其添加接口时,可以使用基于继承的静态代理。
- 当有多个类或者多个方法都需要附加相同的功能时,可以使用动态代理。
- 动态代理又分为JDK动态代理和GCLIB动态代理。JDK动态代理需要原始类定义了接口;GCLIB动态代理用来解决原生类没有定义接口的情况,其本质是通过字节码处理工具ASM来转换字节码并生成新类。
- 代理模式可以应用在开发一些非业务需要上,如监控、统计、权限、限流、事务和日志;代理模式也可以应用在PRC和缓存中。