Spring系列学习六、深入Spring AOP——揭开代理的神秘面纱

深入Spring AOP——揭开代理的神秘面纱

  • 一、动态代理的实现原理
  • 二、CGLIB字节码增强的实现原理
  • 三、结语

上一章节,我们体验了Spring AOP强大的能力的同时,是不是也想弄明白,它是怎么原理是什么呢?如果自己要做一个类似的框架,应该怎么做呢? 带着这样的疑问我们一起来深入学习下。生活中其实也有很多类似的情形,比如名星,一般都会有经纪人,由经纪人负责接洽各种工作,名星只在活动开始时参加即可。这种貌似拉皮条的,一般我们称之谓代理。 当我们说到代理, 你是不是马上想到那些出国旅游, 想翻墙看网页的场景?实际上,在编程世界中,代理默默的在背后给我们提供了强大的支撑,让我们能够可以优雅的战斗。今天,我们就要揭开Spring AOP中代理既神秘又迷人的面纱,为大家展现它的魅力和威力。

代理的主要作用就是在不改变目标对象的情况下,对目标对象的方法进行增强。JAVA中代理通常有两种实现方式,动态代理和CGLIB字节码增强代理,下面我们逐个介绍。

一、动态代理的实现原理

首先,我们要了解一个重要的角色,他就是Java的原生代理-动态代理。
假设我们有一个顾客(Customer)和一个代购(Agent)。顾客有一系列购物需求,但没有时间去购物,所以他把购物需求告诉代购,让代购帮他完成购物。这就是动态代理的实现原理,我们创建了一个代理对象(Agent),这个对象与被代理的对象(Customer)具有相同的行为。
下面我们用Java的动态代理来实现这个例子,把动态代理的魅力通过代码来展示。

public interface Shopping {
    void buyThings();
}

public class Customer implements Shopping {
    @Override
    public void buyThings() {
        System.out.println("The customer is buying things...");
    }
}

public class AgentHandler implements InvocationHandler {
    private Shopping customer;
    public AgentHandler(Shopping customer) {
        this.customer = customer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("The agent starts to buy things...");
        Object result = method.invoke(customer, args);
        System.out.println("The agent finishes buying things...");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Shopping customer = new Customer();
        AgentHandler handler = new AgentHandler(customer);
        // 如下是手工调用,一般都是框架自动完成,我们使用时注入原始类型即可。
        Shopping agent = (Shopping)Proxy.newProxyInstance(
                Shopping.class.getClassLoader(),
                new Class[]{Shopping.class},
                handler
        );
        agent.buyThings();
    }
}

在这段代码中,我们创建了一个Customer实例,并通过一个AgentHandler, 为它生成了一个代理的代购Agent。然后调用agent的buyThings方法时,实际上就是调用了handler中的invoke方法,我们在这个方法中实现了AOP, 对Customer购物行为进行了增强:在购物前后打印出开始和结束的标志,并实际执行了购物的行为。
运行结果如下:
在这里插入图片描述

请注意, Java的动态代理的限制是只能对接口进行代理,不能对具体的类进行代理,也就是说动态代理适用于类有实现接口的情况下,可以看到动态代理本质上是产生了一个类(代购),而这个类依赖于真实的对象(顾客)。

如果我们的目标类没有实现接口,那该怎么办呢? 别担心,Spring AOP帮我们解决了这个问题。

二、CGLIB字节码增强的实现原理

一般来说一个人变成另外一个人,可能就只有科幻电影中的情节了,但是在Spring AOP的世界里,CGLIB可以帮我们做到这一点。CGLIB利用字节码技术,为我们生成新的类,这些类不但继承了原类的所有功能,还增加了一些新的功能,就像原来的类突然变成了超人一样强大。
让我们用代码来看看CGLIB是如何实现这个功能的:

public class Customer2 {
    public void buyThings() {
        System.out.println("The customer is buying things...");
    }
}

public class AgentMethodInterceptor implements MethodInterceptor {
    private Object customer;
    public AgentMethodInterceptor(Object customer) {
        this.customer = customer;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("The agent starts to buy things...");
        Object result = proxy.invoke(customer, args);
        System.out.println("The agent finishes buying things...");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Customer2.class);
        enhancer.setCallback(new AgentMethodInterceptor(new Customer2()));
        Customer2 customer = (Customer2) enhancer.create();
        customer.buyThings();
    }
}

这段代码基本和之前的动态代理代码相似,首先创建一个Customer2实例,然后为它创建一个代理。区别在于,这次的代理是基于类的代理,而不仅仅是接口的代理,运行结果如下:
在这里插入图片描述
从Test2的代码中,我们可以看到,Cgilib这种增加,实际上是在运行期创建了目标类的子类对象,而子类对象是个代理对象,包含了目标类,同时加入了增强的内容(Callback),从而实现了代理功能(对目标对象运行时增加)。

三、结语

代理的两种实现方式,通过两个例子,我们已经讲清楚了; 通过这两个例子,其实很容易就能懂得,每种代理的应用场景。比方说,如果你的目标对象实现了接口,动态代理是您的第一选择(当然使用Cglib也没问题的)。反之,如果你的目标对象没有实现接口,CGLIB字节码增强方式是您的不二选择。
这篇文章为您深入洞察了Spring AOP背后的运行原理。掌握一项技术,不仅仅是要知道如何使用,而且要深入了解其背后的原理,才能游刃有余地使用它并发挥出它的最大效用。希望这篇文章能够照亮您攀爬学习Spring的险峻山峰的道路,让我们一起加油!

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

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

相关文章

阿里云服务器ECS入门与基础运维

一、云服务器简介 1、服务器: (1) 概念: 服务器本身就是一种电脑,同样具备CPU、内存、硬盘、网卡、电源等硬件。 互联网对外提供网站、游戏、在线会议、网盘等服务,都需要将这些互联网服务部署到服务器中。 (2) 特点&#xf…

Fluids —— DOP Nodes

目录 Gas SubStep —— 重复执行对应的子步 Switch Solver —— 切换解算器 Gas Attribute Swap —— 交换、复制或移动几何体属性 Gas Intermittent Solve —— 固定时间间隔计算子解算器 Gas External Forces —— 计算外部力并更新速度或速度场 Gas Particle Separate…

python学习笔记

四、列表 4.1 序列的索引及切片操作 s"helloworld" # 正向递增 for i in range(0,len(s)):print(i,s[i],end\t\t) print(\n) # 反向递减 for i in range(-len(s),0):print(i,s[i],end\t) print(\n) # 切片 for i in range(0,5,2):print(i,s[i],end\t)4.2 序列的相关…

Vue中Vuex的环境搭建和原理分析及使用

Vuex的环境搭建 Vuex是Vue实现集中式数据管理的Vue的一个插件,集中式可以理解为一个老师给多个学生讲课。 Vue2.0版本的安装: npm i vuex3 使用Vuex需要在store中的index.js引入Vuex和main.js中引入store,目的是让vm和vc都能看到$store。实现多个组件…

快速实现产品智能:用 AI 武装你的 API | 开源日报 No.138

openchatai/OpenCopilot Stars: 3.8k License: MIT OpenCopilot 是一个允许你拥有自己产品的 AI 副驾驶员的项目。它集成了产品底层 API,并可以在需要时执行 API 调用。它使用 LLMs 来确定用户请求是否需要调用 API 端点,然后决定调用哪个端点并根据给定…

Retrieval-Augmented Generation for Large Language Models: A Survey

PS: 梳理该 Survey 的整体框架,后续补充相关参考文献的解析整理。本文的会从两个角度来分析总结,因此对于同一种技术可能在不同章节下都会有提及。第一个角度是从整体框架的迭代来看(对应RAG框架章节),第二个是从RAG中…

SPON世邦 IP网络对讲广播系统 多处文件上传漏洞复现

0x01 产品简介 SPON世邦IP网络对讲广播系统是一种先进的通信解决方案,旨在提供高效的网络对讲和广播功能。 0x02 漏洞概述 SPON世邦IP网络对讲广播系统 addscenedata.php、uploadjson.php、my_parser.php等接口处存在任意文件上传漏洞,未经身份验证的攻击者可利用此漏洞上…

6.OpenResty系列之深入理解(二)

1. 日志输出 vim /usr/local/openresty/nginx/conf/nginx.conf默认配置如下 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;http {#log_format main $remote_addr - $remote_user [$time…

提高企业培训考试系统的用户体验的技术技巧

随着现代企业培训的发展,企业培训考试系统已经成为企业人力资源培训的重要工具。为了提高用户体验,需要采取一些技术技巧来优化系统功能和界面设计。 第一,用户界面的简洁性是提高用户体验的关键。在设计考试系统界面时,应该尽量避…

flutter中枚举的使用

Dart 2.17 增加了对枚举成员变量的支持,推荐使用方式三 使用dart工具来运行代码,工具:https://dartpad.cn //方式一:未支持扩展枚举时 enum InOutOrderStatusEnum {approval,completed,cancel,rejected;int get statusCode {sw…

CSS3渐变属性详解

渐变属性 线性渐变 概念:线性渐变,指的是在一条直线上进行的渐变。在线性渐变过程中,起始颜色会沿着一条直线按顺序过渡到结束颜色 语法: background:linear-gradient(渐变角度,开始颜色,结束颜色);渐变…

FineBI实战项目一(4):指标分析之每日订单总额/总笔数

1 明确数据分析目标 统计每天的订单总金额及订单总笔数 2 创建用于保存数据分析结果的表 use finebi_shop_bi;create table app_order_total(id int primary key auto_increment,dt date,total_money double,total_cnt int ); 3 编写SQL语句进行数据分析 selectsubstring(c…

DeepPurpose 生物化学深度学习库;蛋白靶点小分子药物对接亲和力预测虚拟筛选

参考: https://blog.csdn.net/c9Yv2cf9I06K2A9E/article/details/107649770 https://github.com/kexinhuang12345/DeepPurpose ##安装 pip install DeepPurpose rdkitDeepPurpose包括: 数据: 关联TDC库下载,是同一作者开发的 https://blog.csdn.net/weixin_42357472/artic…

一、二进制方式 安装部署K8S

目录 一、操作系统初始化 1、关闭防火墙 2、关闭 SELinu 3、 关闭 swap 4、添加hosts 5、同步系统时间 二、集群搭建 —— 使用外部Etcd集群 1、自签证书 2、自签 Etcd SSL 证书 ① 创建 CA 配置文件:ca-config.json ② 创建 CA 证书签名请求文件&#xff…

Python3 字典

字典是另一种可变容器模型,且可存储任意类型对象。 字典的每个键值 key>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,格式如下所示: d {key1 : value1, key2 : value2, key3 : value3 } 注…

[计算机提升] 设置文件关联程序

4.4 设置文件关联程序 我们知道,系统是通过文件的后缀名来选择使用哪种程序来打开同一种类型的文件的。这个我们可以在选中文件后,点击鼠标右键,在弹出的菜单中选择属性: 可以在打开方式中看到默认是用记事本程序打开.docx文件…

【读书笔记】《白帽子讲web安全》跨站脚本攻击

目录 前言: 第二篇 客户端脚本安全 第3章 跨站脚本攻击(XSS) 3.1XSS简介 3.2XSS攻击进阶 3.2.1初探XSS Payload 3.2.2强大的XSS Payload 3.2.2.1 构造GET与POST请求 3.2.2.2XSS钓鱼 3.2.2.3识别用户浏览器 3.2.2.4识别用户安装的软…

【C++】STL 算法 ⑥ ( 二元谓词 | std::sort 算法简介 | 为 std::sort 算法设置 二元谓词 排序规则 )

文章目录 一、二元谓词1、二元谓词简介2、 std::sort 算法简介3、 代码示例 - 为 std::sort 算法设置 二元谓词 排序规则 一、二元谓词 1、二元谓词简介 " 谓词 ( Predicate ) " 是一个 返回 布尔 bool 类型值 的 函数对象 / 仿函数 或 Lambda 表达式 / 普通函数 , …

【小沐学C++】C++ 实现鼠标键盘钩子HOOK

文章目录 1、简介2、相关函数2.1 SetWindowsHookEx2.2 UnhookWindowsHookEx2.3 CallNextHookEx 3、相关结构体3.1 KBDLLHOOKSTRUCT3.2 MSLLHOOKSTRUCT 4、挂钩过程5、代码测试5.1 代码1 结语 1、简介 https://learn.microsoft.com/zh-cn/windows/win32/winmsg/about-hooks 挂…

Avalonia学习(二十)-登录界面演示

今天开始继续Avalonia练习。 本节:演示实现登录界面 在网上看见一个博客,展示Avalonia实现,仿照GGTalk,我实现了一下,感觉是可以的。将测试的数据代码效果写下来。主要是样式使用,图片加载方式。 只有前…