《深入理解 AOP》

一、AOP 是什么

AOP(Aspect Oriented Programming),即面向切面编程,是软件开发中一种重要的编程范式。它通过横向抽取机制,将那些与业务逻辑本身无关、却为业务模块所共同调用的逻辑或责任(如事务处理、日志管理、权限校验等)封装起来,然后通过“动态植入”的方式嵌入到业务逻辑的指定位置,从而实现业务逻辑的隔离与解耦。

AOP 是面向对象编程(OOP)的补充。在 OOP 中,我们通过类和继承来组织代码,但在某些情况下,会遇到一些问题。例如,当需要为多个不具有继承关系的对象添加公共方法时,如日志记录、性能监控等,如果采用 OOP 的方式,就需要在每个对象中都添加相同的方法,这会导致大量的重复代码,增加维护成本。而 AOP 则可以很好地解决这个问题,它将这些公共逻辑抽取出来,集中管理,避免了重复代码的产生。

二、AOP 的优势

  1. 减少重复代码:将公共逻辑集中到切面中,避免了在多个地方重复编写相同的代码。

  2. 提高开发效率:开发者可以专注于业务逻辑的实现,而无需在每个地方都处理那些公共的、与业务逻辑无关的逻辑。

  3. 方便维护:当需要修改公共逻辑时,只需修改切面中的代码,而无需修改每个使用该逻辑的地方。

三、AOP 的技术要点

(一)通知(Advice)

通知定义了“什么时候”和“做什么”。它包含了需要用于多个应用对象的横切行为。根据通知的执行时机,可以分为以下几种类型:

  1. 前置通知(@Before):在目标方法调用之前调用通知。

  2. 后置通知(@After):在目标方法完成之后调用通知。

  3. 环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法。需要注意的是,目标对象的方法需要手动执行。

  4. 返回通知(@AfterReturning):在目标方法成功执行之后调用通知。

  5. 异常通知(@AfterThrowing):在目标方法抛出异常之后调用通知。

(二)连接点(Join Point)

连接点是程序执行过程中能够应用通知的所有点。在 Spring 中,连接点指的是方法,因为 Spring 只支持方法类型的连接点。

(三)切点(Pointcut)

切点定义了在“什么地方”进行切入,哪些连接点会得到通知。切点表达式用于明确指定方法的返回类型、类名、方法名和参数名等与方法相关的部件。常用的切点表达式格式为:execution([修饰符] 返回值类型 包名.类名.方法名(参数))。其中,修饰符可以省略,返回值类型、包名、类名、方法名和参数都可以使用通配符(*..)来表示。

(四)切面(Aspect)

切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——是什么、何时、何地完成功能。

(五)引入(Introduction)

引入允许我们向现有的类中添加新方法或者属性。

(六)织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。织入分为编译期织入、类加载期织入和运行期织入。

四、AOP的底层原理

一、AOP 的底层原理概述

在 Spring 框架中,AOP 的实现依赖于动态代理技术。动态代理技术允许在运行时动态地创建代理对象,并在代理对象上调用方法时插入额外的逻辑(即通知)。Spring AOP 主要使用两种动态代理技术:JDK 动态代理和 CGLIB 代理。

二、JDK 动态代理技术

JDK 动态代理是 Java 提供的一种标准代理机制,它依赖于 Java 的反射机制。JDK 动态代理的核心是 java.lang.reflect.Proxy 类和 InvocationHandler 接口。以下是 JDK 动态代理的实现步骤:

(一)为接口创建代理类的字节码文件

  1. 定义接口:首先,需要定义一个接口,目标对象和代理对象都将实现这个接口。例如:

    public interface UserService {void save();
    }
  2. 实现目标类:目标类实现了上述接口,并提供了具体的业务逻辑。例如:

     
    public class UserServiceImpl implements UserService {@Overridepublic void save() {System.out.println("业务层:保存用户...");}
    }
  3. 创建代理类:通过 java.lang.reflect.Proxy 类动态生成代理类。代理类实现了与目标类相同的接口,并在方法调用时插入额外的逻辑。例如:

    public class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在目标方法执行前插入逻辑System.out.println("前置通知:记录日志");// 执行目标方法Object result = method.invoke(target, args);// 在目标方法执行后插入逻辑System.out.println("后置通知:记录日志");return result;}
    }
  4. 生成代理实例:通过 Proxy.newProxyInstance 方法动态生成代理实例。例如:

    UserService userService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), // 目标类的类加载器new Class<?>[]{UserService.class}, // 目标类实现的接口new MyInvocationHandler(new UserServiceImpl()) // 自定义的 InvocationHandler
    );

(二)使用 ClassLoader 将字节码文件加载到 JVM

  1. 类加载器的作用ClassLoader 负责加载字节码文件到 JVM 中。在 JDK 动态代理中,Proxy.newProxyInstance 方法会使用目标类的类加载器来加载生成的代理类。

  2. 动态生成字节码Proxy 类会在运行时动态生成代理类的字节码,并通过类加载器加载到 JVM 中。代理类的字节码是基于目标类实现的接口动态生成的。

(三)创建代理类实例对象,执行对象的目标方法

  1. 代理类实例:通过 Proxy.newProxyInstance 方法生成的代理类实例对象,可以像普通对象一样调用接口方法。

  2. 方法调用:当调用代理类实例的方法时,实际上会调用 InvocationHandlerinvoke 方法。在 invoke 方法中,可以插入前置通知、后置通知等逻辑,并最终调用目标方法。

三、CGLIB 代理技术

CGLIB(Code Generation Library)是一个强大的字节码生成库,它可以在运行时动态生成目标类的子类,并覆盖目标类的方法。CGLIB 代理主要用于那些没有实现接口的类,或者需要代理类的方法而不是接口的方法。

(一)CGLIB 代理的基本原理

  1. 动态生成子类:CGLIB 通过字节码操作库(如 ASM)动态生成目标类的子类。生成的子类继承了目标类,并覆盖了目标类的方法。

  2. 方法拦截:在覆盖的方法中,CGLIB 提供了一个 MethodInterceptor 接口,用于拦截方法调用。在拦截器中,可以插入额外的逻辑,并最终调用目标方法。

(二)CGLIB 代理的实现步骤

  1. 定义目标类:目标类不需要实现接口,例如:

    public class UserServiceImpl {public void save() {System.out.println("业务层:保存用户...");}
    }
  2. 创建拦截器:实现 MethodInterceptor 接口,定义拦截逻辑。例如:

     
    public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在目标方法执行前插入逻辑System.out.println("前置通知:记录日志");// 执行目标方法Object result = proxy.invokeSuper(obj, args);// 在目标方法执行后插入逻辑System.out.println("后置通知:记录日志");return result;}
    }
  3. 生成代理实例:通过 Enhancer 类动态生成代理实例。例如:

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(UserServiceImpl.class); // 设置目标类
    enhancer.setCallback(new MyMethodInterceptor()); // 设置拦截器
    UserServiceImpl userService = (UserServiceImpl) enhancer.create();
  4. 使用代理实例:通过代理实例调用方法时,实际上会调用拦截器的 intercept 方法,在其中插入额外的逻辑,并最终调用目标方法。

四、JDK 动态代理与 CGLIB 代理的比较

表格
特性JDK 动态代理CGLIB 代理
适用场景目标类必须实现接口目标类可以没有接口
代理方式通过接口代理通过生成子类代理
性能相对较好,因为基于接口调用稍差,因为需要生成子类并覆盖方法
灵活性只能代理接口方法可以代理类的方法,更灵活
实现机制基于 Java 反射和 InvocationHandler基于字节码操作库(如 ASM)

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

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

相关文章

Python【协程(Coroutine)和线程的关系】

协程(Coroutine)和线程都是实现并发编程的技术&#xff0c;但它们在实现方式、使用场景和性能上有显著区别。理解它们的关系与差异有助于在实际应用中选择合适的并发模型&#xff0c;以下是它们的核心关系与对比分析&#xff1a; 一、核心关系 互补关系 协程和线程可以结合使用…

Springboot——Redis的使用

在当今的软件开发领域&#xff0c;缓存技术是提升应用性能的关键手段之一。Redis 作为一款高性能的键值对存储数据库&#xff0c;凭借其出色的读写速度和丰富的数据结构&#xff0c;在缓存场景中得到了广泛应用。Spring Boot 作为一款简化 Spring 应用开发的框架&#xff0c;与…

BEVPoolv2:A Cutting-edge Implementation of BEVDet Toward Deployment

背景 该论文是在BEVDet的基础上进行了一个调整优化&#xff0c;传统的方法是将特征图与深度预测进行外积得到视椎特征图&#xff0c;再将它与预处理好的体素索引结合&#xff0c;将每个视椎特征分类到每个voxel中进行累加和的操作。BEVFusion与BEVDepth等方法是避免了累加和&a…

蓝桥杯常考的找规律题

目录 灵感来源&#xff1a; B站视频链接&#xff1a; 找规律题具有什么样的特点&#xff1a; 报数游戏&#xff08;Java组&#xff09;&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路详解&#xff1a; 代码详解&#xff1a; 阶乘求和&#xff08;Java组…

使用ffmpeg 将图片合成为视频,填充模糊背景,并添加两段音乐

1.输入3张图片,每张播放一次,播放两秒,视频分辨率设置为1920:1080,每张图片前0.3秒淡入,后0.3秒淡出,图片宽高比不变,用白色填充空白区域 ffmpeg -loop 1 -t 2 -i "img1.jpg" \-loop 1 -t 2 -i "img2.jpg" \-loop 1 -t 2 -i "img3.jpg" \-filte…

PostgreSQL技术内幕29:事件触发器tag原理解析

文章目录 0.简介1.概念说明2.tag的生成和存储2.1 tag合法性校验2.2 内存中存储2.3 持久化存储 3.tag的触发 0.简介 在上一篇文章中中&#xff0c;我们介绍了PG中的两种触发器&#xff0c;即适合于DML的普通触发器和对于DDL的事件触发器&#xff0c;其中事件触发器与常规的 DML…

mysql 导入很慢,如何解决

精选 原创 码出财富2025-04-14 17:35:14博主文章分类&#xff1a;数据库©著作权 文章标签mysql数据库用户名文章分类MySQL数据库yyds干货盘点阅读数184 导入大型 SQL 文件到 MySQL 数据库时&#xff0c;速度可能会受到影响。以下是一些优化方法和建议&#xff0c;帮助你…

多物理场耦合低温等离子体装置求解器PASSKEy2

文章目录 PASSKEy2简介PASSKEY2计算流程PASSKEy2 中求解的物理方程电路模型等离子体模型燃烧模型 PASSKEy2的使用 PASSKEy2简介 PASSKEy2 是在 PASSKEy1 的基础上重新编写的等离子体数值模拟程序。 相较于 PASSKEy1&#xff0c; PASSKEy2 在具备解决低温等离子体模拟问题的能力…

保姆级zabbix监控jmx、数据库和网络监控(SNMP)

前言 在当今数字化时代&#xff0c;企业IT基础设施的稳定性与性能直接关系到业务连续性和用户体验。随着系统复杂性的不断增加&#xff0c;单一维度的监控已难以满足全面运维需求。Zabbix作为一款功能强大的开源监控解决方案&#xff0c;通过整合JMX&#xff08;Java Manageme…

复杂地形越野机器人导航新突破!VERTIFORMER:数据高效多任务Transformer助力越野机器人移动导航

作者&#xff1a; Mohammad Nazeri 1 ^{1} 1, Anuj Pokhrel 1 ^{1} 1, Alexandyr Card 1 ^{1} 1, Aniket Datar 1 ^{1} 1, Garrett Warnell 2 , 3 ^{2,3} 2,3, Xuesu Xiao 1 ^{1} 1单位&#xff1a; 1 ^{1} 1乔治梅森大学计算机科学系&#xff0c; 2 ^{2} 2美国陆军研究实验室&…

SharpMap与TerraLib:C#与C++开源GIS库

大家好&#xff0c;今天为大家介绍的软件是SharpMap&#xff1a;一款专为了C#&#xff08;.NET&#xff09;环境设计的开源地图和空间数据处理库&#xff1b;TerraLib&#xff1a;一款由C编写、支持多种数据库的开源的GIS软件库。 下面&#xff0c;我们将从两个开源软件的主要…

音视频学习 - MP3格式

环境 JDK 13 IDEA Build #IC-243.26053.27, built on March 16, 2025 Demo MP3Parser MP3 MP3全称为MPEG Audio Layer 3&#xff0c;它是一种高效的计算机音频编码方案&#xff0c;它以较大的压缩比将音频文件转换成较小的扩展名为.mp3的文件&#xff0c;基本保持源文件的音…

Unity中数据和资源加密(异或加密,AES加密,MD5加密)

在项目开发中&#xff0c;始终会涉及到的一个问题&#xff0c;就是信息安全&#xff0c;在调用接口&#xff0c;或者加载的资源&#xff0c;都会涉及安全问题&#xff0c;因此就出现了各种各样的加密方式。 常见的也是目前用的最广的加密方式&#xff0c;分别是&#xff1a;DE…

部署本地deepseek并在调用的详细步骤以及解决一些可能出现的问题(Windows,Linux, WSL)

打开Ollama官网&#xff1a;https://ollama.com/ 直接下载Ollama并且安装好Ollama、这时候就能看到app里多了个ollama&#xff0c;但是我们不用打开它 打开Windows Powershell&#xff1a; ollama run deepseek-r1:1.5b 7b 8b 14b 32b 70b 根据自己的电脑配置和需求更换不同的…

【KWDB 创作者计划】_嵌入式硬件篇---寄存器与存储器截断与溢出

文章目录 前言一、寄存器与存储器1. 定义与基本概念寄存器(Register)位置功能特点存储器(Memory)位置功能特点2. 关键区别3. 层级关系与协作存储层次结构协作示例4. 为什么需要寄存器性能优化指令支持减少总线竞争5. 其他寄存器类型专用寄存器程序计数器(PC)栈指针(SP)…

小白自学python第二天

学习python的第二天 一、判断语句 1、布尔类型和比较运算符 1、布尔类型 表示现实生活中的逻辑&#xff0c;真&#xff08;True&#xff0c;用数字1表示&#xff09;和假&#xff08;False&#xff0c;用数字0表示&#xff09; 2、布尔类型变量的定义 变量的名称 布尔类…

linux基础操作1------(文件命令)

一.前言 我们本章开始讲解linux&#xff0c;我们对于linux得有重要的认识&#xff0c;比如项目部署等等&#xff0c;都会用到linux&#xff0c;今天我们就开始linux的学习&#xff0c;我们需要准备的工具有vmware和xshell&#xff0c;而这里我就不教大家虚拟机的安装以及xshel…

编码问题整合

一、windows系统编码 查看编码命令&#xff1a;chcp - 936 GBK - 65001 UTF-8 - 437 英文修改系统编码 1、控制面板修改 需管理员权限-Windows 10/11进入 控制面板 > 区域 > 管理 > 更改系统区域设置勾选 Beta版: 使用Unicode UTF-8提供全球语言支持 → 重启生效修…

如何配置Spark

1.上传spark安装包到某一台机器&#xff08;自己在finaShell上的机器&#xff09;。 2.解压。 把第一步上传的安装包解压到/opt/module下&#xff08;也可以自己决定解压到哪里&#xff09;。对应的命令是&#xff1a;tar -zxvf 安装包 -C /opt/module 3.重命名。进入/opt/mo…

Redis 完整配置模板

一、基础连接配置&#xff08;单机模式&#xff09; 基础参数&#xff08;适用Spring Boot&#xff09; spring:redis:host: 127.0.0.1port: 6379password: your_passworddatabase: 0 # 默认DB索引timeout: 2000ms # 全局操作超时时间二、连接池参数&#xff08;通用核心配…