Spring框架的AOP实现原理

article/2025/10/3 13:44:37

一、AOP的基本概念

AOP先是一种思想,后是一种技术。

        AOP:面向切面编程,是将那些与业务无关(比如有事务处理,日志管理,权限控制等),但要为业务模块共同调用的逻辑封装成一个可重用的模块,这个模块被称为切面,便于减少系统的重复代码,降低模块间的耦合度,有利于未来的可拓展性和可维护性。如下图所示:

 

        Target:所谓target就是横切新代码的对象

        Joinpoint:连接点,就是在Target上具体的执行点,与Pointcut匹配

        Aspect:切面,其中包装着Advice和Pointcut,是通过Bean注册的基本单位

        Pointcut:切入点,与Joinpoint对应,定义了切面逻辑的执行地点

        Advice:是具体的切入逻辑,切面完成的工作包含在Advice中

        Weaving:织入,把切面加入到对象中,并创建出代理对象的过程

织入时期:(如下表所示)

时期理解
编译期切面在目标类编译时被织入,需要特殊的编译器,Aspect的织入编译器以这种方式织入切面
类加载期切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader),可以在目标类引入应用之前增强目标类的字节码
运行期切面在应用的某个时期被织入,在织入切面时,AOP容器会为目标对象动态创建一个代理对象

AOP的通知类型:

        1.前置通知:实现MethodBeforeAdvice接口,在目标方法调用前,执行通知。

        2.环绕通知:实现MethodInterceptor接口,是一个包围目标方法的通知。此通知可以在方法调用前后完成自定义的行为。

        3.后置通知:实现AfterReturningAdvice接口,在目标方法调用后,执行通知,如果方法跑出来异常,那么不执行通知。

        4.异常通知:实现ThrowsAdvice接口,在方法抛出异常的时候,执行通知。

二、Spring的AOP基于动态代理实现

       1. 首先,如果被代理的对象,已经实现了某个接口,那么AOP会使用反射,通过接口的方式来创建代理对象。jdk实现动态代理需要两个组件,一个为InvocationHandler接口,需要编写一个类去实现这个接口,然后重写invoke()方法;

另一个为Proxy类,我们可以通过这个类的newProxyInstance()方法,返回一个代理对象,生成的代理类实现了原来类的所有接口,然后对这些接口进行代理,通过代理对象调用这些方法,底层通过反射来调用我们实现的invoke()方法。

       2. 如果被代理的对象没有实现某个接口,无法通过反射去进行动态代理,这个时候AOP要使用Cglib,基于继承的方式生成一个被代理对象的子类来作为代理。也就是说Cglib要实现一个MethodInteceptor,方法调用会被转发到该类的intercept()方法,然后在需要使用目标对象的时候,通过Cglib动态代理来获取代理对象。

三、两种动态代理的区别

        jdk方式:采用反射的方式,只能对实现接口的类生成代理,加载速度快,执行效率低。

        Cglib方式:通过字节码形式实现,针对类实现代理,加载速度满,但执行效率高。

四、AOP的作用

AOP采用横向抽取机制,取代了传统纵向继承机制的重复性代码,应用主要体现在事务处理,日志管理,权限控制,异常处理等方面。

主要作用:分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

总结的来说就是AOP主要是保证开发者在不修改源代码的前提下,为系统中的业务逻辑组件添加某种通用功能。

五、代码实现

@Aspect // 面向切面
@Component // UserAOP 注解为Bean
public class UserAOP {@Pointcut("execution(* com.yjy.service.impl.*.*(..))")public void pointcut() {}@Before("pointcut()")public void beforeAdvice(JoinPoint point) {System.out.println("前置增强:" + point.getSignature().getName() + "方法。");}@AfterReturning(value = "pointcut()", returning = "obj")public Object afterReturnAdvice(Object obj) throws Throwable {// 被增强方法的返回结果System.out.println("基于Spring AOP的后置返回增强处理.....结果:" + obj);return obj;}@After("pointcut()")public void afterAdvice(JoinPoint point) {System.out.println("基于Spring AOP的最终增强处理.....方法:" + point.getSignature().getName());}@Around("pointcut()")public Object aoundAdvice(ProceedingJoinPoint jp) {System.out.println("环绕增强:" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法,参数:" + Arrays.toString(jp.getArgs()));Object res = null;try {res = jp.proceed();//调用目标方法  返回目标方法的返回结果} catch (Throwable e) {System.out.println("环绕增强 catch:" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法,参数:" + Arrays.toString(jp.getArgs()) + "发生异常:" + e.getMessage());} finally {System.out.println("基于Spring AOP的环绕增强中finally处理.....");}return res;}@AfterThrowing(value = "pointcut()", throwing = "e")public void afterThrowAdvice(JoinPoint p, RuntimeException e) {
//        System.out.println("基于Spring AOP的异常返回增强处理.....");System.out.println("异常增强:" + p.getSignature().getName() + "发生了异常" + e.getMessage());}}

​​​​public interface UserService {void queryAll();int addUser();}
@Service("userServiceImpl")
public class UserServiceImpl implements UserService {@Overridepublic void queryAll() {System.out.println("执行了查询所有的业务");// 抛出异常
//        throw new RuntimeException("没事..测试");}@Overridepublic int addUser() {System.out.println("执行了 新增");
//        if (true) {
//            throw new RuntimeException("没事..测试");
//        }return 1;}
}
public class TestAop {@Testpublic void TestAop() {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) ac.getBean("userServiceImpl");userService.addUser();}
}

运行结果:

 

 


http://chatgpt.dhexx.cn/article/f6Yvpncm.shtml

相关文章

Java:由浅入深揭开 AOP 实现原理

概述: 最近在开发中遇到了一个刚好可以用AOP实现的例子,就顺便研究了AOP的实现原理,把学习到的东西进行一个总结。文章中用到的编程语言为kotlin,需要的可以在IDEA中直接转为java。 这篇文章将会按照如下目录展开: A…

Spring的AOP原理

为什么80%的码农都做不了架构师?>>> 一、什么是 AOP AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完…

利用C语言实现动态数组

数组存在的问题:如果我们定义一个数组去存储数据,需要提前定义数组的个数,或者数组根据第一次存储的元素个数自动确定数组的大小,但是我们如果想对数组进行元素插入只能重新定义一个新数组,或者预定义一个空间非常大的…

C++数组之动态数组

目录 1.摘要 2.动态数组内存分配 1一维数组 2多维数组(以2维为例) 3.动态数组初始化 1默认初始化 2.自定义初始化 4.动态数组释放 5.例子 Gradebook类的实现 6.参考文章 1.摘要 数组是一种顺序存储的数据结构,在定义数组时&…

C++中如何定义动态数组

首先:为什么需要动态定义数组呢? 这是因为,很多情况下,在预编译过程阶段,数组的长度是不能预先知道的,必须在程序运行时动态的给出 但是问题是,c要求定义数组时,必须明确给定数组…

c++ 动态数组

动态数组 相关数组知识连接 数组详解 多维数组 在之前的文章中,讲解了数组的相关知识,那一种数组(数组相关连接:https://blog.csdn.net/m0_62870588/article/details/123787052)又称是静态数组,因为它的大小…

C语言中动态分配数组

很多人在编写C语言代码的时候很少使用动态数组,不管什么情况下通通使用静态数组的方法来解决,在当初学习C语言的时候我就是一个典型的例子,但是现在发现这是一个相当不好的习惯,甚至可能导致编写的程序出现一些致命的错误。尤其对…

C语言学习笔记:动态数组

动态数组 数组是C语言中的很重要的一种构造类型,最初我们学习的都是静态数组,但是,静态数组有着自己难以改变的缺点——数组长度固定。 一般在静态数组定义后,系统就会为其分配对应长度的连续的专有内存空间,可是&am…

C语言如何实现动态数组?

提出问题 请问在c语言里如何实现动态大小的数组啊,比如说int a[N];,这里N的值可以在程序中定,或者有什么方法可以实现类似的功能?总之只要在编译时不用制定数组大小就行。 分析问题 嵌入式系统的内存是宝贵的,内存是否…

C的动态数组的详细知识(网上收集到的大量详细知识以及个人理解的汇总)

动态数组是指在声明时没有确定数组大小的数组,即忽略圆括号中的下标;当要用它时,可随时用ReDim语句重新指出数组的大小。使用动态数组的优点是可以根据用户需要,有效利用存储空间。 可以了解动态数组的详细定义 一.C版本动态数组…

动态数组C语言实现详解

目录 0、前言 一、动态数组数据结构 二、动态数组增删改查函数声明 三、数组创建 1、头部动态创建 2、头部静态创建 四、元素添加 五、元素删除 1、根据元素值删除 2、根据元素位置删除 六、元素修改 七、元素查找 八、数组清空 九、数组销毁 十、验证程序 0、前…

C语言实现 动态数组 处理任意类型数据

引言:动态数组在C/C、Java、Python等语言中应用广泛,高级语言一般通过调用类或接口等可以快捷使用,C语言实现动态数组需要手动构造,以下为实现过程。 1 结构体构造动态数组 typedef struct Array {void **p; //维护在堆区…

C语言创建动态数组

C语言创建动态数组 1.编写步骤 1. 添加所需头文件 stdlib.h 该头文件下包含的与分配存储区相关的函数如下: void* malloc (size_t size);//从堆中分配size字节的存储空间 void* calloc (size_t num, size_t size);//分配数组并将数组零初始化。为 num 个元素的数…

在OpenCV里实现开运算

前面学习腐蚀和膨胀算法,并且深刻地认识到它们的特性以及作用。如果由这两种组合出来的运算又有什么样的不同呢?比如一个图像先腐蚀后膨胀的操作,会有什么结果呢?因为腐蚀是把图片白色变小,膨胀又是把图片白色变大,是否会保持原图不变呢?带着这些问题来研究一下先腐蚀后…

OpenCV python 形态学 圆形开运算

处理流程 # -*- coding: utf-8 -*- # note : 形态学 开运算 圆形内核 处理 # --------------------------------import cv2 as cv import numpy as npdef opening_circle(img_bin, kernel_size10):# 形态学kernel np.zeros((kernel_size, kernel_size), np.uint8)center_…

腐蚀、膨胀、开运算、闭运算

一、腐蚀、膨胀、开运算、闭运算 腐蚀:图像中的高亮部分进行膨胀 膨胀:原图中的高亮部分被腐蚀,类似于领域被蚕食 开运算:先腐蚀再膨胀,可以去掉目标外孤立的点 闭运算:先膨胀再腐蚀,可以去掉目…

【youcans 的 OpenCV 例程200篇】137. 灰度开运算和灰度闭运算原理

欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中 欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中 【youcans 的 OpenCV 例程200篇】137. 灰度开运算和灰度闭运算 5. 灰度级形态学 灰度级形态学将形态学操作从二值图像扩展到灰度图…

开闭运算

开运算和闭运算是将腐蚀和膨胀按照一定的次序进行处理。但这两者并不是可逆的,即先开后闭并不能得到原来的图像。 开运算 开运算是先腐蚀后膨胀,其作用是:分离物体,消除小区域。特点:消除噪点,去除小的干扰…

图像的形态学开操作(开运算)和闭操作(闭运算)的概念和作用,并用OpenCV的函数morphologyEx()实现对图像的开闭操作

大家看这篇博文前可以先看一看下面这篇博文,下面这篇博文是这篇博文的基础: 详解图像形态学操作之图形的腐蚀和膨胀的概念和运算过程,并利用OpenCV的函数erode()和函数dilate()对图像进行腐蚀和膨胀操作 图像形态学腐蚀可以将细小的噪声区域去除&#x…

OpenCV-Python图像运算变换处理:开运算和闭运算以及不同核矩阵的影响分析

☞ ░ 前往老猿Python博客 https://blog.csdn.net/LaoYuanPython ░ 一、引言 在《OpenCV-Python图像处理:腐蚀和膨胀原理及erode、dilate函数介绍 https://blog.csdn.net/LaoYuanPython/article/details/109441709》等系列博文中老猿详细介绍了腐蚀和膨胀的原理、…