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

news2024/11/18 9:40:59

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

什么是动态代理?

动态代理(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 功能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编程考古-计算机发展(中)

晶体管计算机时代 尽管真空管技术标志着计算机步入了现代化的门槛,但其固有的局限性——庞大的体积、高昂的能耗、频繁的故障以及不菲的成本——极大地阻碍了其普及与实际应用。 晶体管的早期 Point-contact transistor 点接触晶体管 1947年,贝尔实验…

vue2+3 —— Day5/6

自定义指令 自定义指令 需求&#xff1a;当页面加载时&#xff0c;让元素获取焦点&#xff08;一进页面&#xff0c;输入框就获取焦点&#xff09; 常规操作&#xff1a;操作dom “dom元素.focus()” 获取dom元素还要用ref 和 $refs <input ref"inp" type&quo…

报错 No available slot found for the embedding model

报错内容 Server error: 503 - [address0.0.0.0:12781, pid304366] No available slot found for the embedding model. We recommend to launch the embedding model first, and then launch the LLM models. 目前GPU占用情况如下 解决办法: 关闭大模型, 先把 embedding mode…

Maven 构建项目

Maven 是一个项目管理和构建工具&#xff0c;主要用于 Java 项目。它简化了项目的构建、依赖管理、报告生成、发布等一系列工作。 构建自动化&#xff1a;Maven 提供了一套标准化的构建生命周期&#xff0c;包括编译、测试、打包、部署等步骤&#xff0c;通过简单的命令就可以执…

【C++笔记】C++三大特性之多态

【C笔记】C三大特性之多态 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】C三大特性之多态前言一.多态1.1 多态的概念1.2 虚函数1.3 虚函数的重写/覆盖1.4 多态的定义及实现 二.虚函数重写的⼀些其他问题2.1 协变(…

【项目实战】基于 LLaMA-Factory 通过 LoRA 微调 Qwen2

【项目实战】基于 LLaMAFactory 通过 LoRA 微调 Qwen2 一、项目介绍二、环境准备1、环境准备2、安装LLaMa-Factory3、准备模型数据集3.1 模型准备3.2 数据集准备 三、微调1、启动webui2、选择参数3、训练 四、测试五、总结 一、项目介绍 LLaMA-Factory是一个由北京航空航天大学…

内容占位符:Kinetic Loader HTML+CSS 使用CSS制作三角形原理

内容占位符 前言 随着我们对HTML和CSS3的学习逐渐深入&#xff0c;相信大家都已经掌握了网页制作的基础知识&#xff0c;包括如何使用HTML标记构建网页结构&#xff0c;以及如何运用CSS样式美化页面。为了进一步巩固和熟练这些技能&#xff0c;今天我们一起来完成一个有趣且实…

SpringSecurity 鉴权认证入门讲解

​ Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 ​ 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比与Sp…

【插件】多断言 插件pytest-assume

背景 assert 断言一旦失败&#xff0c;后续的断言不能被执行 有个插件&#xff0c;pytest-assume的插件&#xff0c;可以提供多断言的方式 安装 pip3 install pytest-assume用法 pytest.assume(表达式,f’提示message’) pytest.assume(表达式,f‘提示message’) pytest.ass…

虾皮:LLM注意力机制的下沉现象分析

&#x1f4d6;标题&#xff1a;When Attention Sink Emerges in Language Models: An Empirical View &#x1f310;来源&#xff1a;arXiv, 2410.10781 &#x1f31f;摘要 &#x1f538;语言模型&#xff08;LM&#xff09;将大量注意力分配给第一个标记&#xff0c;即使它在…

MyBatis的select标签的resultType属性

在MyBatis框架中&#xff0c;映射文件中select标签的resultType属性&#xff0c;用于指定从数据库查询返回结果集需要映射的Java类型&#xff0c;即Mapper接口中方法返回值类型(或集合中的泛型类型)&#xff0c;可以是基本数据类型、基本数据类型的包装类型、自定义的PO类型、集…

ubuntu20.04如何升级python3.8到python3.10

主要参考了这两个链接&#xff1a; 如何在Ubuntu 20.04安装Python 3.10 | myfreaxhttps://www.myfreax.com/how-to-install-python-3-10-on-ubuntu-20-04/#:~:text%E5%9C%A8%E8%B0%83%E8%AF%95%E5%92%8C%E5%85%B6%E4%BB%96%E5%B7%A5%E5%85%B7%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%B…

AWTK-WIDGET-WEB-VIEW 发布

awtk-widget-web-view 是通过 webview 提供的接口&#xff0c;实现的 AWTK 自定义控件&#xff0c;使得 AWTK 可以方便的显示 web 页面。 项目网址&#xff1a; https://gitee.com/zlgopen/awtk-widget-web-view webview 提供了一个跨平台的 webview 接口&#xff0c;是一个非…

丹摩征文活动|FLUX.1+ComfyUI部署与使用

丹摩征文活动&#xff5c;FLUX.1ComfyUI部署与使用 1.引言 在人工智能飞速发展的今天&#xff0c;丹摩智算平台&#xff08;DAMODEL&#xff09;以其卓越的AI算力服务脱颖而出&#xff0c;为开发者提供了一个简化AI开发流程的强大工具。通过租赁GPU资源&#xff0c;丹摩智算平…

性能高于Transformer模型1.7-2倍,彩云科技发布基于DCFormer架构通用大模型云锦天章

2017年&#xff0c;谷歌发布《Attention Is All You Need》论文&#xff0c;首次提出Transformer架构&#xff0c;掀开了人工智能自然语言处理&#xff08;NLP&#xff09;领域发展的全新篇章。Transformer架构作为神经网络学习中最重要的架构&#xff0c;成为后来席卷全球的一…

【异常解决】Linux shell报错:-bash: [: ==: 期待一元表达式 解决方法

博主介绍&#xff1a;✌全网粉丝21W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…

Linux解决普通用户无法使用sudo指令的问题

问题描述&#xff1a; Linux解决普通用户无法使用sudo指令的问题 sudo 指令是允许 普通用户 临时 以 超级用户 root 的权限运行。 普通用户如果没有配置而直接使用 sudo 指令&#xff1a;系统会提示没有权限&#xff08;如下图&#xff09; 使用sudo时系统提示&#xff08;当前…

9.1 使用haarcascade_frontalface_default.xml分类器对静态图像进行人脸检测。

1&#xff09;程序代码&#xff1a; # 1. 使用haarcascade_frontalface_default.xml分类器对静态图像进行人脸检测。 import cv2 import numpy as np # 构造级联分类器对象face_cascade cv2.CascadeClassifier(./data/haarcascades/haarcascade_frontalface_default.xml# ./…

【Mysql】Mysql函数----字符串函数

1、字符串函数 函数 描述 示例 CHAR_LENGTH(S) 返回字符串S的字符个数 返回字符串runoob的字符个数&…

(干货)Jenkins使用kubernetes插件连接k8s的认证方式

#Kubernetes插件简介 Kubernetes 插件的目的是能够使用 Kubernetes 配合&#xff0c;实现动态配置 Jenkins 代理&#xff08;使用 Kubernetes 调度机制来优化负载&#xff09;&#xff0c;在执行 Jenkins Job 构建时&#xff0c;Jenkins Master 会在 kubernetes 中创建一个 Sla…