设计模式学习笔记(三、结构型-代理模式)
目录:
- 什么是代理模式
- 为什么要有代理模式
- 如何实现代理模式
- 代理模式应用场景
什么是代理模式
在不改变原始类(被代理类)代码的情况下,通过代理类给被代理类增强的一种模式,这种模式叫做代理模式。
为什么要有代理模式
代理模式可以让被代理类不变更的情况下,使其拥有更多附加功能,使其只关注核心功能,忽略掉附加功能。让代码更简洁,更易维护。
如何实现代理模式
1、静态代理:
首先我们先看一段代码
1 public class UserController { 2 3 /** 4 * 通过构造函数注入 5 */ 6 private MetricsCollector metricsCollector; 7 8 public void login(String mobile, String password) { 9 long start = System.currentTimeMillis(); 10 // 这里是登录的业务逻辑 11 long end = System.currentTimeMillis(); 12 long resTime = end - start; 13 metricsCollector.record("/login", resTime); 14 } 15 16 public void register(String mobile, String password) { 17 long start = System.currentTimeMillis(); 18 // 这里是注册的业务逻辑 19 long end = System.currentTimeMillis(); 20 long resTime = end - start; 21 metricsCollector.record("/register", resTime); 22 } 23 }
上面这段代码包含了登录的注册逻辑,且他们都做了统计接口响应时间的逻辑。
很明显这两个接口都做了同意的附加功能,这样不仅会影响代码的可读性,也会在修改统计逻辑的时候花费较大的成本。
像这种情况我们可以通过代理模式很好的解决这个场景了,如下。
1 public interface IUserController { 2 3 void login(String mobile, String password); 4 5 void register(String mobile, String password); 6 }
1 public class UserController implements IUserController { 2 3 /** 4 * 通过构造函数注入 5 */ 6 private MetricsCollector metricsCollector; 7 8 @Override 9 public void login(String mobile, String password) { 10 // 这里是登录的业务逻辑 11 } 12 13 @Override 14 public void register(String mobile, String password) { 15 // 这里是注册的业务逻辑 16 } 17 }
1 public class UserControllerProxy implements IUserController { 2 3 private UserController userController; 4 private MetricsCollector metricsCollector; 5 6 public UserControllerProxy(UserController userController) { 7 this.userController = userController; 8 this.metricsCollector = new MetricsCollector(); 9 } 10 11 @Override 12 public void login(String mobile, String password) { 13 long start = System.currentTimeMillis(); 14 userController.login(mobile, password); 15 long end = System.currentTimeMillis(); 16 long resTime = end - start; 17 metricsCollector.record("/login", resTime); 18 } 19 20 @Override 21 public void register(String mobile, String password) { 22 long start = System.currentTimeMillis(); 23 userController.register(mobile, password); 24 long end = System.currentTimeMillis(); 25 long resTime = end - start; 26 metricsCollector.record("/register", resTime); 27 } 28 }
1 public class Test { 2 3 public static void main(String[] args) { 4 IUserController userController = new UserControllerProxy(new UserController()); 5 } 6 }
很简单,只需要新建一个IUserController,让业务类UserController和代理类UserControllerProxy实现它即可。
然后通过IUserController iUserController = new UserControllerProxy(new UserController())使用即可。
但并不是所有的接口都是自己维护的,可能你需要对第三方的功能进行扩展,此时不能定义接口来扩展了,那咋办嘛。其实只要使用继承就可以了,代码与上述类似,这里不再赘述。
———————————————————————————————————————————————————————
2、动态代理:
上述代理模式的实现方式是通过实现接口和继承的方式来达到代理的目的,但如果要需要对其它接口增加统计响应时间的功能,就需要新增代理类。
也就是每加一个被代理类就需要加一个代理类,这样非常麻烦,而且也不好维护。
上述的这种加代理类的方式,我们称之为静态代理。
针对静态代理的缺点,我们可以通过动态代理来解决,也就是在运行的过程中动态的创建代理类,替换掉原始类。
在Java中已经提供了动态代理的语法,所以很容易就能实现,如下:
1 public class MetricsCollectorProxy { 2 3 private MetricsCollector metricsCollector; 4 5 public MetricsCollectorProxy() { 6 this.metricsCollector = new MetricsCollector(); 7 } 8 9 /** 10 * 创建代理类 11 * 12 * @param target 被代理类 13 * @return 代理类 14 */ 15 public Object createProxy(Object target) { 16 ClassLoader classLoader = target.getClass().getClassLoader(); 17 Class<?>[] interfaces = target.getClass().getInterfaces(); 18 DynamicProxyHandler proxyHandler = new DynamicProxyHandler(target); 19 return Proxy.newProxyInstance(classLoader, interfaces, proxyHandler); 20 } 21 22 private class DynamicProxyHandler implements InvocationHandler { 23 private Object proxiedObject; 24 25 DynamicProxyHandler(Object proxiedObject) { 26 this.proxiedObject = proxiedObject; 27 } 28 29 @Override 30 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 31 long start = System.currentTimeMillis(); 32 Object result = method.invoke(proxiedObject, args); 33 long end = System.currentTimeMillis(); 34 long resTime = end - start; 35 String apiName = proxiedObject.getClass().getName() + ":" + method.getName(); 36 metricsCollector.record(apiName, resTime); 37 return result; 38 } 39 } 40 }
1 public class Test { 2 3 public static void main(String[] args) { 4 MetricsCollectorProxy metricsCollectorProxy = new MetricsCollectorProxy(); 5 IUserController proxy = (IUserController) metricsCollectorProxy.createProxy(new UserController()); 6 } 7 }
通过Proxy.newProxyInstance(classLoader, interfaces, proxyHandler)实现动态代理。
代理模式应用场景
1、业务系统中非功能性的开发,如监控、统计、鉴权、限流、事务、幂等、日志等。
2、接口缓存:两套代码,一套正常查库,一套走redis、内存这类的缓存,通过动态代理实现,让调用者选择模式。