目录

大橙子

VX:ZzzChChen
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:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

实例

package com.zxy.demo.aspect;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @description: study
 * 模块名称:
 * 说明: 测试切面
 * 作者(@author): zxy
 * 创建日期: 2022年1月17日14:53:33
 */

@Aspect
@Component
public class TestAspect {
    @Pointcut("@annotation(com.zxy.demo.annotations.AsyncBizLog)")
    private void servicePointcut() {
    }

    @Before("servicePointcut()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("-------------------方法进入时-------------------");
        System.out.println("参数:" + joinPoint.getArgs()[0]);
    }
    @AfterReturning(value = "servicePointcut()", returning = "retValue")
    public void doAfter(JoinPoint joinPoint, Object retValue) {
        System.out.println("-------------------方法返回后-------------------");
        System.out.println("方法返回值:" + JSON.toJSONString(retValue));
    }

    @AfterThrowing(pointcut = "servicePointcut()", throwing = "ex")
    public void doAfterThrow(Throwable ex) {
        System.out.println("-------------------方法抛出异常-------------------");
    }

    @After(value = "servicePointcut()")
    public void doAfter() {
        System.out.println("-------------------方法结束-------------------");
    }

}

package com.zxy.demo.annotations;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AsyncBizLog {
}

注解

@Pointcut

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

execution表达式:

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

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

annotation() 表达式:

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

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

然后使用该切面的话,就会切入注解是 @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://www.dczzs.com/articles/2022/01/19/1642573826824.html