目录
概述
AOP即面向切面编程(Aspect-Oriented Programming),它是spring框架的第二大核心(第一大核心是IOC控制反转),它是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。AOP是面向对象编程(OOP)的补充,它通过预编译方式(如AspectJ)或运行时动态代理(如Spring AOP)实现程序功能的统一维护。
简单来说,AOP就是面向特定方法编程。可以减少重复代码、提高开发效率。在不修改业务逻辑代码的情况下,通过动态代理技术对已有方法进行增强,实现无侵入式的代码复用。
入门
场景:项目中部分功能运行较慢,需要定位执行耗时较长的业务方法,此时需要统计每一个业务方 法的执行耗时。我们一般会想到,在方法开始前记录时间,在方法结束时再记录一次,这样两次时间的差就是方法执行的时间。这对于一两个方法是可以用的,但如果有几十甚至几百个方法呢?
我们不可能对每个方法都进行改造。此时我们可以抽离出记录时间的逻辑,单独创建一个模板方法。每个业务方法运行时,都去运行这个模板方法,这样就能简单快速的记录每个业务方法的运行时间。
实现:动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
使用:导入AOP依赖,注解@Aspect表示这是一个AOP类,注解@Around表示针对这个类进行编程,如果要统计别的类执行时间,更换这个类即可。这里 * 号表示对service包下所有类都统计执行时间
这时我们在浏览器发送请求后,就能统计到方法耗时:
这样我们没有修改原有代码,也实现了对方法统计时间的功能,这就是AOP面向切面编程,这只是AOP的冰山一角。它还可以记录操作日志、项目权限控制、事务管理等。spring的事务管理底层就是基于AOP完成的。
概念
连接点(Joinpoint):指程序中可以被切面插入的点,如方法调用或属性访问。即可以被AOP控制的方法(暗含方法执行时的相关信息),比如案例中的service包下所有的方法就是连接点
通知(Advice):在连接点处执行的代码,包括前置通知(Before Advice)、后置通知(After Advice)、返回通知(After Returning Advice)、异常通知(After Throwing Advice)和环绕通知(Around Advice) 。也就是重复的逻辑、共性的功能
切入点(Pointcut):用于定义哪些连接点上应该应用通知,通常通过切入点表达式来指定。
切面(Aspect):表示一个模块化的横切关注点,它包括了连接点和通知。可以通过配置文件或注解等方式定义切面。
织入(Weaving):将切面应用到目标对象并创建新的代理对象的过程,可以在编译时、类加载时或运行时完成。
AOP执行流程:
springAOP基于动态代理技术:程序运行时会为目标对象生成一个代理对象,再将通知结合代理对象,就完成了功能增强。此时程序将不再运行原有的对象,而是运行这个代理对象
AOP类如下:
程序运行时通过@Autowired注入的对象其实是代理对象,这个代理对象已经完成了方法增强,这样运行list方法时,就能统计出方法耗时了。
通知
另外,@PointCut注解的作用是将公共的切点表达式抽取出来,需要用时引入用该切点表达式即可
通知顺序:当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。
这里定义了多个切面类,执行顺序如下:
可以看到,不同切面类中,默认按照切面类的类名字母排序。目标方法前的通知方法是字母排名靠前的先执行,而目标方法后的通知方法是字母排名靠前的后执行
@order注解可以控制切面类的执行顺序,对于前通知,括号内数字小的先执行,后通知则相反
切入点表达式
切入点表达式(Pointcut Expression)是AOP中用于定义哪些方法将被增强(即哪些方法将被切面所拦截)的一种表达式语言。它允许开发者指定一组特定的连接点,这些连接点可以是方法的调用、方法的执行、属性的访问等。常见形式如下:
1. execution(......): 根据方法的签名来匹配
2.annotattion(......) : 根据注解匹配
书写建议:
- 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是find开头,更新类方法都是update开头。
- 描述切入点方法通常基于接口描述,而不是直接描述实现类以增强拓展性。
- 在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用...,使用*匹配单个包。
连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。对于@Around 通知,获取连接点信息只能使用 ProceedingJoinPoint。对于其他四种通知, 获取连接点信息只能使用JoinPoint ,它是ProceedingJoinPoint 的父类型