AOP 是什么
全名 Aspect Oriented Programming
,即面向切面编程
一个非常形象的描述,应用了 aop 的程序就和汉堡一样,aop 的代码就是汉堡最外边的两片面包,程序就是汉堡中心的馅料。
不过,我们的 aop 程序可以选择是包在上,或者是包在下边,或者是上下都包裹
如何定义一个 AOP
定义一个标准的 class,然后为这个 class 标记如下两个注解:
@Aspect,表示这是一个标准的 spring 切面类
@Component,表示这个需要交给 spring 进行生命周期的管理
多个 AOP 的执行顺序
如果对执行的顺序有要求,需要在对应的切面类上添加第三个注解 @Order
代码示例:
@Aspect
@Component
@Order(1)
public class HttpResultAspect {
复制代码
其中,order 的值越小,切面执行的顺序就越早,因为切面的特性,会有如下的多个切面的执行示意图
AOP 拦截表达式
常规写法
execution(* *.*(..))
示例讲解
execution (* com.rlzz.r9.mapper.*Mapper.findAll(..))
第一个'*',任意类型返回值
第二个'*',任意包路径(包路径中两点,表示当前包及其子包)
第三个'*',任意方法名
小括号中的两点,任意参数
复制代码
示例2
比如,我们需要编写表达式拦截 com.wb.UserService.findAll()
,下面是一些可行的表达式
execution(* com..*(..))
execution(* com.wb.*(..))
execution(* com.wb.*Service.*(..))
execution(* com.wb.*Service.find*(..))
execution(* com.wb.*Service.findAll(..))
execution(* com.wb..find*(..))
复制代码
应用于方法上的完整示例代码
@Around("execution (* com.rlzz.r9.mapper.*Mapper.update(..))"
+ " || execution (* com.rlzz.r9.mapper.*Mapper.save(..))")
public <D extends RcsDto> D afterSaveOrUpdate(ProceedingJoinPoint joinPoint) {
}
复制代码
使用多个表达式
可以使用逻辑判断符 或 -> ||, 与 -> &&
,将多个表达式串起来判定是否需要拦截目标方法
@PointCut
表达式一般都是直接编写于方法上,当我们需要对表达式进行复用时,可以使用 @PointCut
将表达式抽出来
上述示例代码可以修改为
@PointCut("execution (* com.rlzz.r9.mapper.*Mapper.update(..))")
private void mapperUpdate(){}
@PointCut("execution (* com.rlzz.r9.mapper.*Mapper.save(..))")
private void mapperSave(){}
@Around("mapperUpdate() || mapperSave()")
public <D extends RcsDto> D afterSaveOrUpdate(ProceedingJoinPoint joinPoint) {
}
复制代码
AOP 执行时机
比较正规的说法,aop 中有 5 种通知
@Before
前置通知,在拦截的方法之前执行
@Before("execution (* com.rlzz.r9.mapper.*Mapper.delete(..))")
public void delete(org.aspectj.lang.JoinPoint joinPoint){}
复制代码
@Around
环绕通知,在拦截方法之前执行,然后需要我们手动去执行目标方法,再手动返回 目标方法执行后的 返回值
@Around("execution (* com.rlzz.r9.mapper.*Mapper.save(..))")
public Object aroundFindAll(org.aspectj.lang.ProceedingJoinPoint joinPoint){
Object obj = joinPoint.proceed(); // 此表示执行拦截方法中的方法体
return obj; // 需要手动将方法执行后的结果返回(可以控制返回结果)
}
复制代码
@After
后置通知,在拦截方法之后执行
@After("execution (* com.rlzz.r9.mapper.*Mapper.update(..))")
public void update(org.aspectj.lang.JoinPoint joinPoint){}
复制代码
@AfterReturning
这个是 @after 的增强注解,能够获取到方法执行后的返回值,我们可以对 返回值 做一些检验判断
如果 返回值 是引用类型,我们甚至还可以修改这个引用对应的值
@PointCut("execution (* com.rlzz.r9.mapper.*Mapper.findAll(..))")
private void mapperFindAll(){}
@AfterReturning(returning = "list", pointcut = "mapperFindAll()")
public void findAll(org.aspectj.lang.JoinPoint joinPoint, Object list){
// Object参数的名称,要和注解中的 returning 值保持一致
}
复制代码
@AfterThrowing
@after 的另一个增强注解,在目标方法执行后,如果有异常抛出,就会被 aop 拦截,但是这种处理与 catch 捕获不同,aop 虽然拦截并处理了异常,但是仍然会传播到上一级的调用者
单个切面通知执行顺序
多个切面通知执行顺序
AOP 拦截注意点
class Study {
public void static f1(){} // (1)
public void f2(){} // (2)
public void f3(){ // (3)
f2();
}
}
复制代码
(1)不拦截静态方法
(2)外部调用时,能拦截
(3)外部调用时,能拦截 f3()
,但不会同时拦截 f3()
中调用的 f2()
作者:安逸的咸鱼
链接:https://juejin.cn/post/6999555610190544932
来源:掘金