AOP
# 1. AOP基础
定义
AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。
# 1.1 典型应用场景
- 记录系统的操作日志
- 权限控制
- 事务管理:我们前面所讲解的Spring事务管理,底层其实也是通过AOP来实现的,只要添加@Transactional注解之后,AOP程序自动会在原始方法运行前先来开启事务,在原始方法运行完毕之后提交或回滚事务
# 1.2 核心概念
① 连接点:JoinPoint
连接点指的是可以被aop控制的方法
② 通知:Advice
指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
③ 切入点:PointCut
匹配连接点的条件,通知仅会在切入点方法执行时被应用
④ 切面:Aspect
描述通知Advice与切入点PointCut的对应关系(通知+切入点) 切面所在的类,我们一般称为切面类(被@Aspect注解标识的类) 描述当前aop程序需要针对于哪个原始方法,在什么时候执行什么样的操作。
④ 目标对象:Target
目标对象指的就是通知所应用的对象,我们就称之为目标对象
Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强
# 2. AOP进阶
# 2.1 通知类型
Spring中AOP的通知类型:
@Around
:环绕通知,此注解标注的通知方法在目标方法前、后都被执行@Before
:前置通知,此注解标注的通知方法在目标方法前被执行@After
:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行@AfterReturning
: 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行@AfterThrowing
: 异常后通知,此注解标注的通知方法发生异常后执行
在使用通知时的注意事项:
- @Around环绕通知需要自己调用
ProceedingJoinPoint.proceed()
来让原始方法执行,其他通知不需要考虑目标方法执行 - @Around环绕通知方法的返回值,必须指定为
Object
,来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的。 @PointCut
注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即
# 2.2 通知顺序
在不同切面类中,默认按照切面类的类名字母排序:
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
使用Spring提供的@Order注解来调整顺序
# 2.3 切入点表达式
作用:主要用来决定项目中的哪些方法需要加入通知
# 2.3.1 execution
execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:(其中带?
的表示可以省略的部分)
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
可以使用通配符描述切入点
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分..
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
注意事项:
- 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。
# 2.3.2 @annotation
匹配多个无规则的方法,比如:list()和 delete()这两个方法
# 2.3.3 对比
- execution切入点表达式
- 根据我们所指定的方法的描述信息来匹配切入点方法,这种方式也是最为常用的一种方式
- 如果我们要匹配的切入点方法的方法名不规则,或者有一些比较特殊的需求,通过execution切入点表达式描述比较繁琐
- annotation 切入点表达式
- 基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了
# 2.4 连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型
对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型