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

news2024/9/24 19:24:37

深入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的险峻山峰的道路,让我们一起加油!

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

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

相关文章

SQL Server注入之攻防技战法

那天下着很大的雨,母亲从城里走回来的时候,浑身就是一个泥人,那一刻我就知道我没有别的选择了 1.Mssql报错注入 0.判断数据库类型 1.爆当前用户名 2.爆版本 3.爆服务器名 4.判断数据库个数 5.获取全部数据库 语句只适合>2005 爆当前数据…

旧电脑搭建NAS

旧电脑可以搭建NAS吗? 可以! 性能好吗? 完全没问题! 简单吗? 轻松上手! 怎吗搭建? 这里:用旧电脑搭建NAS在您的家庭中,通过将旧 PC 转变为NAS服务器,您…

Winform中使用Fleck实现Websocket服务端并读取SQLite数据库中数据定时循环群发消息

场景 Winform中使用Websocket4Net实现Websocket客户端并定时存储接收数据到SQLite中: Winform中使用Websocket4Net实现Websocket客户端并定时存储接收数据到SQLite中-CSDN博客 Winform中操作Sqlite数据增删改查、程序启动时执行创建表初始化操作: Wi…

BLE Mesh蓝牙组网技术详细解析之Access Layer访问层(六)

目录 一、什么是BLE Mesh Access Layer访问层? 二、Access payload 2.1 Opcode 三、Access layer behavior 3.1 Access layer发送消息的流程 3.2 Access layer接收消息的流程 3.3 Unacknowledged and acknowledged messages 3.3.1 Unacknowledged message …

轻松上手:Postman Interceptor 插件使用指南

什么是 Postman? Postman 是一种用于测试和开发 API 的工具,让开发者可以轻松地构建、发送、调试 HTTP 请求,并检查响应结果。通过Postman,开发者可以在不编写代码的情况下快速测试 API 的正确性和可靠性。Postman 还支持协作和自…

ubuntu18.04安装MySQL

1.安装mysql服务器端 sudo apt-get -y install mysql-server(18.04/20.04不会提示输入密码,默认是没有密码) 2.安装mysql客户端 sudo apt-get -y install mysql-client3.安装mysql模块 sudo apt-get -y install libmysqlclient-dev4.验证是…

融资项目——全局统一日志说明

通过日志可以查看程序的运行信息和异常信息等,便于维护。日志级别分为TRACE、DEBUG、INFO、WARN、ERROR级别,越往后打印的日志信息越少,如ERROR 级别只会在程序运行出错时才会打印日志。可在application.properties中设置日志级别。 logging…

Python+OpenGL绘制3D模型(七)制作3dsmax导出插件

系列文章 一、逆向工程 Sketchup 逆向工程(一)破解.skp文件数据结构 Sketchup 逆向工程(二)分析三维模型数据结构 Sketchup 逆向工程(三)软件逆向工程从何处入手 Sketchup 逆向工程(四&#xf…

最新Tomcat下载安装详细教程

Tomcat下载安装教程 Tomcat简介Tomcat下载tomcat安装验证安装是否成功 Tomcat简介 Tomcat是什么? Tomcat是web容器。你在做web项目时,多数需要http协议,也就是基于请求和响应,比如你在百度输入一行内容搜索,那么百度服…

一文讲清数据资产入表实操

《中共中央 国务院关于构建数据基础制度更好发挥数据要素作用的意见》已发布一年,数据资产化和入表已成为2023年的热门话题,随着2023年底国家数据局吹风《"数据要素x"三年行动计划(2024-2026年)》即将发布,这…

Java_IO流(字节流)

一、IO流(字节流) 1.1 IO流概述 在前面已经学习过File类。知道File只能操作文件,但是不能操作文件中的内容。我们也学习了字符集,不同的字符集存字符数据的原理是不一样的。有了前面两个知识的基础,接下来我们再学习…

Git(3):Git环境常用命令

1 获取本地仓库 要使用Git对我们的代码进行版本控制,首先需要获得本地仓库 (1)在电脑的任意位置创建一个空目录(例如test)作为我们的本地Git仓库 (2)进入这个目录中,点击右键打开…

NSSCTF sql

开启环境: ?wllm1 回显正常,试试?wllm1 出现报错;加上%23正常 ?wllm-1or 11%23出现过滤 测试,空格用**替代, 等号用like替代 测试长度 ?wlmm1order/**/by/**/3%23正常 ?wlmm1order/**/by/**/4%23报错 长度为3,测试回显位置: ?wlmm-1union/**/select/**/1,2,3%23 …

c++ / day06

1. 利用模板类完成顺序表(两天时间&#xff0c;今天至少写出大致框架) 代码 //implement template in sqlist #include <iostream> #include <cstring>#define MAXSIZE 100using namespace std;template <typename T> class Sqlist {unsigned int len 0;T…

【普中开发板】基于51单片机音乐盒LCD1602显示( proteus仿真+程序+设计报告+讲解视频)

【普中开发板】基于51单片机音乐盒LCD1602显示( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;P08 1. 主要功能&#xff1a; 基于51单片机AT89C51/52&#…

环境准备-VMware安装

照顾到很多人不是很会环境搭建,我这里会将搭建的步骤讲的细致点 第一步,VMware下载。目的是通过VMware搭建Linux服务器,因为大家大部分还是Windows的电脑,我们先下载虚拟机搭建一个Linux系统的服务器 下载完成之后,点击安装,如下: 点击“下一步” 勾选“我接受许可协议…

app store里面的构建版本在线上传

开发苹果ios应用&#xff0c;无论是用原生开发、用hbuilderx开发还是用其他h5框架开发的app&#xff0c;都需要将打包好的ipa文件上传到app store。 在上架app store的过程中&#xff0c;我们会遇到下图的这样一个问题&#xff1a; 就是它要求我们上传一个构建版本&#xff0c…

IDEA断点调试

IDEA断点调试 断点调试是一种在程序执行过程中暂停执行并逐步检查代码状态的方法。它允许开发者在程序运行到特定位置时暂停执行&#xff0c;查看变量的值、执行过程和调用栈等信息&#xff0c;从而更好地理解代码的运行情况和解决问题。可以帮助我们查看java底层源代码的执行…

分布微服软件体系快速云端架构

1 概述 分布微服软件体系云端架构平台&#xff0c;以主流的NACOS服务器作为注册配置中心&#xff0c;采用主流的Gradle框架&#xff0c;内嵌Tomcat10以上版本&#xff0c;用于快速构造各类基于JDK17以上的信息应用系统的分布式微服务软件体系架构&#xff0c;可以适用关系型SQ…