目录

chen 的个人博客

VX:TiAmo151028
Phone:13403656751
Email:zxydczzs@gmail.com

X

Spring AOP实践

概念详解

  1. Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为 execution 方式和 annotation 方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
  2. Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
  3. Aspect:切面,即 Pointcut 和 Advice。
  4. Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
  5. Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

实例

 1package com.zxy.demo.aspect;
 2
 3import com.alibaba.fastjson.JSON;
 4import org.aspectj.lang.JoinPoint;
 5import org.aspectj.lang.annotation.*;
 6import org.springframework.stereotype.Component;
 7
 8/**
 9 * @description: study
10 * 模块名称:
11 * 说明: 测试切面
12 * 作者(@author): zxy
13 * 创建日期: 2022年1月17日14:53:33
14 */
15
16@Aspect
17@Component
18public class TestAspect {
19    @Pointcut("@annotation(com.zxy.demo.annotations.AsyncBizLog)")
20    private void servicePointcut() {
21    }
22
23    @Before("servicePointcut()")
24    public void doBefore(JoinPoint joinPoint) {
25        System.out.println("-------------------方法进入时-------------------");
26        System.out.println("参数:" + joinPoint.getArgs()[0]);
27    }
28    @AfterReturning(value = "servicePointcut()", returning = "retValue")
29    public void doAfter(JoinPoint joinPoint, Object retValue) {
30        System.out.println("-------------------方法返回后-------------------");
31        System.out.println("方法返回值:" + JSON.toJSONString(retValue));
32    }
33
34    @AfterThrowing(pointcut = "servicePointcut()", throwing = "ex")
35    public void doAfterThrow(Throwable ex) {
36        System.out.println("-------------------方法抛出异常-------------------");
37    }
38
39    @After(value = "servicePointcut()")
40    public void doAfter() {
41        System.out.println("-------------------方法结束-------------------");
42    }
43
44}
45
 1package com.zxy.demo.annotations;
 2
 3import java.lang.annotation.*;
 4
 5@Retention(RetentionPolicy.RUNTIME)
 6@Target({ElementType.METHOD})
 7@Documented
 8public @interface AsyncBizLog {
 9}
10

注解

@Pointcut

  1. @Pointcut 注解,用来定义一个切点,即上文中所关注的某件事情的入口,切入点定义了事件触发时机。
  2. @Pointcut 注解指定一个切点,定义需要拦截的东西,这里介绍两个常用的表达式:一个是使用 execution(),另一个是使用 annotation()。

execution 表达式:

以 execution(* com.mutest.controller...(..))) 表达式为例:

  1. 第一个 * 号的位置:表示返回值类型,* 表示所有类型。
  2. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,在本例中指 com.mutest.controller 包、子包下所有类的方法。
  3. 第二个 * 号的位置:表示类名,* 表示所有类。
  4. *(..):这个星号表示方法名, 表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

annotation() 表达式:

annotation() 方式是针对某个注解来定义切点,比如我们对具有 @PostMapping 注解的方法做切面,可以如下定义切面:

1@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
2public void annotationPointcut() {}
3

然后使用该切面的话,就会切入注解是 @PostMapping 的所有方法。这种方式很适合处理 @GetMapping、@PostMapping、@DeleteMapping 不同注解有各种特定处理逻辑的场景。

@Around

@Around 注解用于修饰 Around 增强处理,Around 增强处理非常强大,表现在:

  1. @Around 可以自由选择增强动作与目标方法的执行顺序,也就是说可以在增强动作前后,甚至过程中执行目标方法。这个特性的实现在于,调用 ProceedingJoinPoint 参数的 procedd()方法才会执行目标方法。
  2. @Around 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值。

Around 增强处理有以下特点:

  1. 当定义一个 Around 增强处理方法时,该方法的第一个形参必须是 ProceedingJoinPoint 类型(至少一个形参)。在增强处理方法体内,调用 ProceedingJoinPoint 的 proceed 方法才会执行目标方法:这就是 @Around 增强处理可以完全控制目标方法执行时机、如何执行的关键;如果程序没有调用 ProceedingJoinPoint 的 proceed 方法,则目标方法不会执行。
  2. 调用 ProceedingJoinPoint 的 proceed 方法时,还可以传入一个 Object[ ]对象,该数组中的值将被传入目标方法作为实参——这就是 Around 增强处理方法可以改变目标方法参数值的关键。这就是如果传入的 Object[ ]数组长度与目标方法所需要的参数个数不相等,或者 Object[ ]数组元素与目标方法所需参数的类型不匹配,程序就会出现异常。

@Around 功能虽然强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的 Before、AfterReturning 就能解决的问题,就没有必要使用 Around 了。如果需要目标方法执行之前和之后共享某种状态数据,则应该考虑使用 Around。尤其是需要使用增强处理阻止目标的执行,或需要改变目标方法的返回值时,则只能使用 Around 增强处理了。

@Before

@Before 注解指定的方法在切面切入目标方法之前执行 ,可以做一些 Log 处理,也可以做一些信息的统计,比如获取用户的请求 URL 以及用户的 IP 地址等等

JointPoint 对象很有用,可以用它来获取一个签名,利用签名可以获取请求的包名、方法名,包括参数(通过 joinPoint.getArgs() 获取)等。

@After

@After 注解和 @Before 注解相对应,指定的方法在切面切入目标方法之后执行,也可以做一些完成某方法之后的 Log 处理。

@AfterReturning

@AfterReturning 注解和 @After 有些类似,区别在于 @AfterReturning 注解可以用来捕获切入方法执行完之后的返回值,对返回值进行业务逻辑上的增强处理。

需要注意的是,在 @AfterReturning 注解 中,属性 returning 的值必须要和参数保持一致,否则会检测不到。该方法中的第二个入参就是被切方法的返回值,在 doAfterReturning 方法中可以对返回值进行增强,可以根据业务需要做相应的封装。

@AfterThrowing

当被切方法执行过程中抛出异常时,会进入 @AfterThrowing 注解的方法中执行,在该方法中可以做一些异常的处理逻辑。要注意的是 throwing 属性的值必须要和参数一致,否则会报错。该方法中的第二个入参即为抛出的异常。


标题:Spring AOP实践
作者:zzzzchen
地址:https://dczzs.com/articles/2022/01/19/1642573826824.html