深入解析动态代理:切点执行之前的方法代理是如何生成的?

上一篇文章《探索微服务中的权限控制:一次线上问题排查的思考》中,我们聊到面向切面编程中的切点可以在指定方法执行前后添加代码块,用于权限的控制。在切点执行时,使用了动态代理生成了对应方法的代理类。今天我们就来剖析下切点执行之前,动态代理是如何生成指定方法的代理的。

什么是动态代理?

动态代理(Dynamic Proxy)是一种在程序运行时动态创建代理对象的机制,它能够在不修改原始类代码的基础上,对目标对象的方法调用进行拦截、增强或修改等操作。

Java中动态代理有哪几种实现方式?

在 Java 中,动态代理主要有以下两种常见的实现方式,我们分别来介绍。

1. 基于Java原生反射机制的动态代理

这种方法也叫基于接口的代理,依托于 java.lang.reflect 包下的相关类和接口来实现,主要用于代理实现了接口的类,其核心是 Proxy 类和 InvocationHandler 接口。

2. 基于CGlib(Code Generation Library)的动态代理

这种方式也叫基于类(继承)的代理。依托CGLib,一个功能很强大的第三方代码生成库,它基于字节码操作技术,通过在运行时动态生成目标类的子类来实现动态代理,这种方式不仅可以代理实现了接口的类,更重要的是能够代理那些没有实现接口的普通类,其核心是Enhancer 类和MethodInterceptor 接口。

后续会专门出博客以最佳实战的方式来剖析这两者的源码

切点的动态代理使用了哪一种?

先说结论,切点的动态代理使用了第一种方式,即基于Java原生反射机制的动态代理。由上文可知,第一种动态代理的实现方式主要用于代理实现了接口的类,那么我们来看实现了JoinPoint接口的类是那个?答案是MethodInvocationProceedingJoinPoint类,即MethodInvocationProceedingJoinPoint作为被代理的类。

核心实现解析

MethodInvocationProceedingJoinPoint类主要用于在Spring AOP中基于方法调用的切面场景中,承载着方法调用相关的各种关键信息以及提供了执行目标方法和控制方法调用流程的能力。

public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {

    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    // 与代理方法调用相关的对象
    private final ProxyMethodInvocation methodInvocation;

    // 方法参数
    private Object[] args;

    // 方法签名
    private Signature signature;

    private SourceLocation sourceLocation;

    // ... 其他字段和方法 ...
    }

MethodInvocationProceedingJoinPoint类中有个methodInvocation属性,它定义了与代理方法调用相关的一系列操作和属性访问方法,它抽象了在 Spring AOP 通过动态代理拦截目标方法调用时涉及到的核心行为和信息获取功能,是连接 Spring AOP 底层代理机制与上层切面逻辑操作的重要纽带,为切面逻辑在处理方法调用这个连接点(JoinPoint)时提供了统一的交互规范。

核心方法解析

下面,我们来详细介绍 MethodInvocationProceedingJoinPoint 类的核心方法及其作用:

MethodInvocationProceedingJoinPoint类核心方法有以下几个:

  1. proceed() 方法
    • 作用:用于触发目标方法的实际执行。在切面逻辑中,尤其是环绕通知(@Around 注解标记的切面逻辑)里经常会用到它。
    • 实现:先克隆当前的 methodInvocation 对象(通过 invocableClone() 方法),然后调用克隆后的对象的 proceed() 方法来启动目标方法的调用流程,克隆操作有助于保证每次执行的独立性以及便于 Spring AOP 对每次调用进行准确管理。
public MethodInvocation invocableClone(Object... arguments) {
        if (this.userAttributes == null) {
            this.userAttributes = new HashMap();
        }

        try {
            ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation)this.clone();
            clone.arguments = arguments;
            return clone;
        } catch (CloneNotSupportedException var3) {
            throw new IllegalStateException("Should be able to clone object of type [" + this.getClass() + "]: " + var3);
        }
    }
  1. getArgs() 方法
  • 作用:用于获取方法调用时的实际参数数组。它采用了懒加载机制,即仅在首次调用该方法且内部缓存的参数数组 args 为空时,才从 methodInvocation 对象中克隆一份参数数组进行缓存并返回
  • 实现
public Object[] getArgs() {
    if (this.args == null) {
        this.args = (Object[])this.methodInvocation.getArguments().clone();
    }
    return this.args;
}
  1. getSignature() 方法
  • 作用:用于获取方法签名对象,同样采用懒加载方式,首次调用时会创建一个内部类 MethodSignatureImpl 的实例作为方法签名返回。方法签名包含了目标方法的诸多关键信息,像方法名、修饰符、参数类型、返回值类型、异常类型等。
  • 实现
public Signature getSignature() {
    if (this.signature == null) {
        this.signature = new MethodSignatureImpl();
    }
    return this.signature;
}
  1. getThis() 方法
  • 作用:返回代理对象本身。在 Spring AOP 中,客户端代码实际调用的是通过动态代理机制生成的代理对象的方法,而切面逻辑围绕着对这个代理对象方法调用的拦截和处理展开。
  • 实现
public Object getThis() {
    return this.methodInvocation.getProxy();
}
  1. getTarget() 方法
  • 作用
    用于获取目标对象,也就是真正提供业务逻辑的那个对象实例。
  • 实现
public Object getTarget() {
    return this.methodInvocation.getThis();
}

小结

通过上述方法,MethodInvocationProceedingJoinPoint 为我们提供了对代理方法调用的全面控制和访问能力。在切面开发中,我们可以使用这些方法来获取方法信息、参数、目标对象等,从而实现丰富的 AOP 功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/917942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【freertos】FreeRTOS信号量的介绍及使用

FreeRTOS信号量 一、概述二、PV原语三、函数接口1.创建一个计数信号量2.删除一个信号量3.信号量释放4.在中断释放信号量5.获取一个信号量,可以是二值信号量、计数信号量、互斥量。6.在中断获取一个信号量,可以是二值信号量、计数信号量7.创建一个二值信号…

【生物服务器】数据分析//论文润色/组学技术服务 、表观组分析、互作组分析、遗传转化实验、生物医学

DNA亲和纯化测序(DAP-seq)和组蛋白甲基化修饰是表观遗传学研究中两个重要的技术手段,它们在揭示基因表达调控机制和染色质结构动态变化中发挥着关键作用。然而,在实践过程中,这两种技术也存在一些痛点和挑战。 DNA亲和…

丹摩征文活动| 摩智云端深度解析:Faster R-CNN模型的训练与测试实战指南

目录 丹摩简介 文章前言Faster R-CNN的简介Faster RCNN的训练与测试提前准备1.1 mobaxterm(远程连接服务器)1.2 本文的源码下载 目标检测模型 Faster-Rcnn2.1云服务器平台 数据上传内置JupyterLab的使用本地连接使用DAMODEL实例获取实例的SSH访问信息通过…

二叉搜索树介绍

⼆叉搜索树 二叉搜索树的概念二叉搜索树的性能分析查找性能插入性能删除性能 二叉搜索树的插入二叉搜索树的查找二叉搜索树的删除⼆叉搜索树的实现代码测试代码 二叉搜索树key和key/value使⽤场景key搜索场景key/value搜索场景key/value⼆叉搜索树代码实现测试代码 二叉搜索树的…

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析

揭秘C语言输入输出内幕:printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾: VS 2022 社区版C语言的安装教程,不要再卡在下载0B/s啦C语言入门:解锁基础概念,动手实现首个C程序C语言概念之旅:解锁关…

5.4.2-1 编写Java程序在HDFS上创建文件

本次实战涉及使用Java操作Hadoop HDFS,包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务,成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践,如检查文件存在性以避免重复创建,以及处理HDFS安全…

RabbitMQ教程:路由(Routing)(四)

文章目录 RabbitMQ教程:路由(Routing)(四)一、引言二、基本概念2.1 路由与绑定2.2 Direct交换机2.3 多绑定2.4 发送日志2.5 订阅 三、整合代码3.1 EmitLogDirectApp.cs3.2 ReceiveLogsDirectApp.cs3.3 推送所有和接收e…

智云-一个抓取web流量的轻量级蜜罐v1.5

智云-一个抓取web流量的轻量级蜜罐v1.5 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN 新增功能-自定义漏洞信息 可通过正则来添加相关路由以及响应来伪造 nacos的版本响应如下 日流量态势 月流量态势 抓取流量效果

21.UE5游戏存档,读档,函数库

2-23 游戏存档、读档、函数库_哔哩哔哩_bilibili 目录 1.存档蓝图 2.函数库 2.1保存存档 2.2读取存档: 3.加载游戏,保存游戏 3.1游戏实例对象 3.2 加载游戏 3.3保存游戏 这一节的内容较为错综复杂,中间没有运行程序进行阶段性成果的验…

实验5:网络设备发现、管理和维护

实验5:网络设备发现、管理和维护 实验目的及要求: 通过实验,掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

vue 项目使用 nginx 部署

前言 记录下使用element-admin-template 改造项目踩过的坑及打包部署过程 一、根据权限增加动态路由不生效 原因是Sidebar中路由取的 this.$router.options.routes,需要在计算路由 permission.js 增加如下代码 // generate accessible routes map based on roles const acce…

DataWorks on EMR StarRocks,打造标准湖仓新范式

在大数据领域,数据仓库和实时分析系统扮演着至关重要的角色。DataWorks 基于大数据引擎,为数据仓库/数据湖/湖仓一体等解决方案提供统一的全链路大数据开发治理平台,为用户带来智能化的数据开发和分析体验。而阿里云提供的 EMR Serverless St…

七、利用CSS和多媒体美化页面的习题

题目一&#xff1a; 利用CSS技术&#xff0c;结合表格和列表&#xff0c;制作并美化 “ 翡翠阁 ”页面。运行效果如下 运行效果&#xff1a; 代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>翡翠阁</title>&…

游戏引擎学习第15天

视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出&#xff08;IO&#xff09;操作的讨论。主要分为两类&#xff1a; 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源&#xff0c;例如音乐、音效、美术资源&#xff08;如 3D 模型和…

【动手学深度学习Pytorch】2. Softmax回归代码

零实现 导入所需要的包&#xff1a; import torch from IPython import display from d2l import torch as d2l定义数据集参数、模型参数&#xff1a; batch_size 256 # 每次随机读取256张图片 train_iter, test_iter d2l.load_data_fashion_mnist(batch_size) # 将展平每个…

51单片机基础05 实时时钟-思路及代码参考2、3

目录 一、思路二 1、原理图 2、代码 二、思路三 1、原理图 2、代码 一、思路二 所有设定功能相关的操作均在矩阵键盘进行实现&#xff0c;并在定时器中扫描、计数等 1、原理图 2、代码 #include <AT89X52.h> //调用51单片机的头文件 //------------------…

Notepad++的完美替代

由于Notepad的作者曾发表过可能在开发者代码中植入恶意软件的言论&#xff0c;他备受指责。在此&#xff0c;我向大家推荐一个Notepad的完美替代品——NotepadNext和Notepad--。 1、NotepadNext NotepadNext的特点&#xff1a; 1、跨平台兼容性 NotepadNext基于Electron或Qt…

Python | Leetcode Python题解之第564题数组嵌套

题目&#xff1a; 题解&#xff1a; class Solution:def arrayNesting(self, nums: List[int]) -> int:ans, n 0, len(nums)for i in range(n):cnt 0while nums[i] < n:num nums[i]nums[i] ni numcnt 1ans max(ans, cnt)return ans

面试经典 150 题:20、2、228、122

20. 有效的括号 参考代码 #include <stack>class Solution { public:bool isValid(string s) {if(s.size() < 2){ //特判&#xff1a;空字符串和一个字符的情况return false;}bool flag true;stack<char> st; //栈for(int i0; i<s.size(); i){if(s[i] ( |…

使用vscode+expo+Android夜神模拟器运行react-native项目

1.进入夜神模拟器安装路径下的bin目录 2.输入命令&#xff0c;连接Android Studio 启动夜神模拟器后&#xff0c; 打开安装目录的bin文件夹执行下面的命令&#xff0c;只需执行一次&#xff09; nox_adb.exe connect 127.0.0.1:62001adb connect 127.0.0.1:62001 3.运行项目…