一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 21 天,点击查看活动详情
日积月累,水滴石穿 😄
AOP 为 Aspect Oriented Programming 的缩写,翻译为面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗理解就是在不修改源代码的情况下,在主干功能里面添加新的功能。
- Advisor:切面,也可以称为 ,由 切点(pointCut)和 建议(advice)组成。
- Advice:单词意思是建议,不过国内程序员一般称为通知。建议切面在何时执行以及如何执行增强处理。说的直白点就是代理逻辑。通知分为如下几种:
- Before :前置通知,在连接点前调用。如果抛出异常则不会流转到连接点。
- After :后置通知,在连接点后调用,不管连接点是否执行正常、异常都会执行的通知。
- AfterReturning:返回通知,在连接点执行正常并返回后调用,执行正常也就是说在执行过程中没有发生异常。
- AfterThrowing:异常通知,当连接点执行发生异常时调用。
- Around:环绕通知,连接点执行的前后可以执行自定义代码。
- Join point:连接点,在 中一个连接点就是一个方法。
- PointCut:切点,一系列连接点的集合,可以通过表达式找到一系列的连接点。
- Target object:目标对象。被一个或多个切面建议的对象,也就是被代理对象。
- AOP proxy:代理对象。由 AOP 框架创建的对象,使用的是JDK 动态代理或 CGLIB 代理。
- Weaving:织入,就是将代理逻辑添加到对目标类具体连接点上,并创建代理对象的过程。
Spring 框架是基于 AspectJ 实现 AOP 操作!但各位需要知道, AspectJ 并不是 Spring 的组成部分,它是一个独立的框架,Spring AOP引入了对 AspectJ 的支持,也建议使用 AspectJ 来开发 AOP。
使用 Spring 实现 AOP 方式有两种,分别如下:
- XML 配置文件实现
- 注解实现 先来看一下不使用 AOP 的代码!
类中就一个注解。
测试代码,运行结果如下:
过了一段时间,项目经理跟你聊天。。。
- 项目经理:我想在 方法中加一个方法执行开始时间和方法执行结束时间吧,但是有个要求不能改动原逻辑!你有什么好注意。
- 你:稍作考虑,我们可以使用 Spring AOP 进行实现。
- 项目经理:怎么使用 AOP 呢?
- 你:我所了解到的方式有两种,分别是 XML 配置文件实现 和 注解实现。
- 项目经理:嗯,看来你胸有成竹了,那这任务就交给你了,别让我失望哦!
- 你:嗯好的,我先写个 demo 给您过目一下。
既然要使用 Spring AOP,那先在 文件中引入相关依赖:
Spring AOP 的依赖就不需要再次加入了,中有 Spring AOP 的依赖。
XML方式的 AOP 配置有许多的标签,在这里进行介绍一下。
- ProductController
- ProductAspect
测试代码,运行结果如下:
接下来就对该方法进行 AOP 增强!
- 1、增加 aop 命名空间
- 2、增加如下配置
测试代码,运行结果如下:
xml 方式可以对重复使用的切点表达式进行提取,使用标签。
- 当 元素作为 元素的子元素定义时,表示它可被多个切面所共享。
- 当 元素作为 元素的子元素时,表示该切入点只对当前切面有效。
上面展示了 定义切面的使用方式。下面介绍标签的使用方式。,定义 AOP 通知器,通知器跟切面一样,也包括通知和切点。但需要注意一点:定义切面时,可以引用普通 bean,而定义时,引用的通知必须实现 Advice 接口。
1、创建名为 的类,实现 , 两个接口!
-
- MethodBeforeAdvice:前置通知,在连接点执行前调用。
-
- AfterReturningAdvice:返回通知,在连接点执行正常并返回后调用。
- 2、在Spring配置文件中增加如下代码:
- 3、启动结果如下:
为了防止与上面的代码冲突,在其 包下,所有类重新进行创建!
在配置类中新增注解,代表开启 @AspectJ 注解支持。
也可以使用 xml 方式开启 AOP 方式,如以下示例所示:
类上, 使用注解表示它将作为一个 Spring Bean 被容器管理,使用注解 表示它是一个切面。
类中有两个方法 、,分别使用 、注解进行标注。代表在目标方法执行前、目标方法执行后执行。具体用法下面介绍。
两个方法都有一个类型为 的参数,这个参数是 提供的,可以用来获取到方法信息。比如:目标方法参数、目标对象等等。这个参数是非必选的。
声明了切点,也就是 ,表明在该切面的切点是这个类中的方法,就是一个连接点。 对于的具体用法下面会简单介绍介绍。
测试代码,运行结果如下:
完美达到项目经理的要求!
注解方式也有 5 种通知类型,分别如下:
- Before :前置通知,在连接点执行前调用。
- After :后置通知,在连接点执行后调用。不管目标方法是否发生异常。 | 属性 | 备注 | | --- | --- | |value| 绑定切入点表达式。可以直接设置切入点表达式,也可以设置切入点声明|
- AfterReturning:返回通知,在连接点执行正常并返回后调用,执行正常也就是说在执行过程中没有发生异常。 | 属性 | 备注 | | --- | --- | |value| 绑定切入点表达式。可以直接设置切入点表达式,也可以设置切入点声明| |pointcut| 跟 value 效果一致,但优先级高于 value| |returning| 将目标方法的返回值绑定到指定的参数名称|
- AfterThrowing:异常通知,当连接点执行发生异常时调用。 | 属性 | 备注 | | --- | --- | |value| 绑定切入点表达式。可以直接设置切入点表达式,也可以设置切入点声明| |pointcut| 跟 value 效果一致,但优先级高于 value| |throwing| 将目标方法发生的异常绑定到指定的参数名称|
- Around:环绕通知,连接点执行之前和之后都可以执行额外代码。 | 属性 | 备注 | | --- | --- | |value| 绑定切入点表达式。可以直接设置切入点表达式,也可以设置切入点声明|
那如何使用呢?在上述例子中已经使用到和 两种通知,接下来将其他类型的通知补全,然后进行测试,观察它们的执行顺序。
测试代码,运行结果如下:
可以发现执行顺序为 。 注意不同的 Spring 版本可能会有差异。比如 5.0.8.RELEASE 和 5.3.17。
环绕通知是五种通知中最复杂、也是最强大的一种,需要传入类型为 参数,这个参数也只能加在环绕通知上。有了这个参数就可以干其他逻辑,比如:获得当前方法的参数、方法、目标对象。
并且还需要显示调用 的 方法。 如果没调用该方法,那执行结果如下 :
- 可以发现并没有将 、、这几句话打印出来,也就是说,如果不调用 方法,则不会执行 前置通知 和 目标方法逻辑、返回通知、后置通知。当然正常来说,也不会如此使用!
根据打印的日志还可以发现最终方法返回值为变为了 。但是在环绕通知里还是有返回值的,这是为什么呢?
因为被标注的方法是 void,那么就需要给 的方法设置返回参数,
测试代码,运行结果如下:
- 上面的例子并没有执行异常通知,那异常通知需要怎么才能被执行呢?需要当连接点执行发生异常时调用。那就在 方法中抛一个异常吧!
测试代码,运行结果如下:
OK,达到目的,返回通知并没有被执行,并且异常通知有执行。
上面我们定义了五种通知,但是五种通知的切入点表达式都是一致的。是不是觉得代码重复率挺高的。对于这种频繁出现的相同的表达式,可以使用 注解声明切点表达式,抽取公用切点表达式,然后在各种通知中进行使用,具体使用如下:
测试代码,运行结果如下:
切入点表达式有什么作用呢?寻找连接点。
上面只用到了其中一种表达式:。
语法结构如下:
结构中带符号的匹配式都是可选的,也就是说必写的只有三个:
- 返回类型匹配
- 方法名匹配
- 参数匹配
还支持通配符
- :匹配所有字符
- :匹配多个包或者多个参数
- :表示类及其子类
- :运算符
可能比较难以理解,下面举几个例子:
- 1、对 类里面的 方法进行增强 结构:
- 2、对 类里面的所有方法进行增强 结构:
- 3、对 包中的所有类、所有方法进行增强 结构:
- 4、对 类里面的 方法或者 进行增强
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/5674.html