• 了解代理模式


    如果大家觉得文章有错误内容,欢迎留言或者私信讨论~

      代理模式的原理与实现比较简单,大多 Java 开发都接触过,它就是在不改变原始代码的情况下,通过引入代理类给原始类附加功能,比如我们举一个非常常见的例子,我们都给业务代码记过时,你肯定写过这样的代码:

    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, startTimes
    	metricsCollector.recordRequest(requestInfo);
    	//...返回UserVo数据...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      很明显,这样的写法是存在在问题的。

    1. 性能计数框架入侵到了业务代码中,跟业务代码高度耦合,如果未来需要替换这个框架,那么代价就会很大
    2. 收集接口请求的代码跟业务代码无关,本就不应该放到一个类中,业务类的职责最好单一、清晰

      这时候我们就需要代理模式将业务代码跟框架代码进行解耦(算是代理模式的精髓)。代理类 UserControllerProxy和原始类 UserController 实现相同的接口 IUserController。UserController 类只负责业务功能。代理类 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, startTimes
    		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, startTi
    		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
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

      不过对于外部类的扩展,我们一般采用的是继承的方式,像是来自第三方的库类,我们没有办法直接修改原始类,给它定义一个接口:

    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();
    		long responseTime = endTimeStamp - startTimestamp;
    		RequestInfo requestInfo = new RequestInfo("login", responseTime, startTimes
    		metricsCollector.recordRequest(requestInfo);
    		return userVo;
    	} 
    	public UserVo register(String telephone, String password) {
    		long startTimestamp = System.currentTimeMillis();
    		UserVo userVo = super.register(telephone, password);
    		long endTimeStamp = System.currentTimeMillis();
    		long responseTime = endTimeStamp - startTimestamp;
    		RequestInfo requestInfo = new RequestInfo("register", responseTime, startTi
    		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
    • 动态代理的原理解析

      虽然说这样能简单实现代理,但是随着而来的问题也有很多。一方面,我们需要在代理类中,将原始类中过度所有方法都是先一边,并且每个方式都附加相似的代码逻辑。另一方面,如果要添加的附加功能的类有不止一个,我们需要针对每个类都创建一个代理类。
      所以我们可以用动态代理来实现这些事情。 因为 Java 语言本身就已经提供了动态代理的语法(实际上,动态代理底层依赖的就是 Java 的反射语法)。我们来看一下,如何用 Java 的动态代理来实现刚刚的功能。具体的代码如下所示。其中,MetricsCollectorProxy 作为一个动态代理类,动态地给每个需要收集接口请求信息的类创建代理类:

    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(), in
    	}
    	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 Exception {
    		long startTimestamp = System.currentTimeMillis();
    		Object result = method.invoke(proxiedObject, args);
    		long endTimeStamp = System.currentTimeMillis();
    		long responseTime = endTimeStamp - startTimestamp;
    		String apiName = proxiedObject.getClass().getName() + ":" + method.getNam
    		RequestInfo requestInfo = new RequestInfo(apiName, responseTime, startTim
    		metricsCollector.recordRequest(requestInfo);
    		return result;
    	}
    	}
    } 
    
    MetricsCollectorProxy proxy = new MetricsCollectorProxy();
    IUserController userController = (IUserController) proxy.createProxy(new UserController);
    
    • 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

      实际上,Spring AOP 底层的实现原理就是基于动态代理。

  • 相关阅读:
    数据结构之栈:使用栈数据结构实现字符串中相邻两个字符不重复
    面试经典150题 -- 二叉树 (总结)
    Spring Framework 核心容器详解:Core、Beans、Context 和 Expression Language 模块
    Java——》MESI
    【软件架构文档】
    构建、标记和发布镜像
    基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的数码管显示应用
    Day 66 二叉树 排序
    【计算机组成 课程笔记】7.2 DRAM和SRAM
    MySQL安装validate_password_policy插件
  • 原文地址:https://blog.csdn.net/qq_43654226/article/details/126473819