Spring Boot AOP详解


Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架中的一个重要特性,用于实现横切关注点的模块化和重用。它通过将横切关注点(如日志记录、性能监控、事务管理等)从业务逻辑中分离出来,以便将它们应用到多个模块中的相同位置,实现代码的重用和解耦。

Spring AOP 的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)和切点(Pointcut)。

  • 切面:切面是一个跨越多个对象的模块化单元,它定义了横切关注点和它们的行为,一个切面可以包含多个通知和切点。
  • 连接点:连接点是在应用程序执行过程中能够插入切面的点,通常连接点是方法的执行点,但也可以是异常抛出时或字段访问时等。
  • 通知:通知是切面在连接点处执行的代码,可以在连接点之前、之后或周围执行,以实现不同的行为。常见的通知类型包括前置通知(在连接点之前执行)、后置通知(在连接点之后执行)、异常通知(在连接点抛出异常时执行)和环绕通知(在连接点前后都执行)。
  • 切点:切点定义了在哪些连接点上应用通知,通过指定切点表达式或使用注解来确定切点的位置。

Spring AOP 可以通过 XML 配置、注解或基于 Java 的配置类来定义切面和通知。它可以与任何 Spring 管理的 bean 一起使用,包括业务对象、服务类、控制器等。在运行时 Spring AOP 使用动态代理技术将通知织入到目标对象的方法中,实现切面的功能。

通过使用 Spring AOP,可以实现横切关注点的集中管理和复用,提高代码的可维护性和灵活性。它可以帮助开发人员解决各种横切关注点的需求,如日志记录、性能监控、事务管理、安全性检查等。

1. 切点定义

Spring 中通过 @Pointcut 定义切点,通过在类上使用 @Aspect 注解开启切面。其基本格式如下:

@Aspect
@Component
public class AspectConfig {
    
    @Pointcut("<Action> (<Pattern> <Return> <Package>(<Params>))")
    public void pointcut() {

    }
}

上述各参数对应描述信息参考下表:

参数 说明
Action 表示作用类型,最常用的为 execution ,后续将详细介绍。
Pattern 指定作用对象的修饰符,如方法的 public 、 private 等,为 * 表示所有。非必填,缺省时为 * 。
Return 指定作用对象的返回类型,为 * 表示所有。
Package 指定作用的对象,通常为方法类所在包,非必填,缺省时为 * 。
Params 指定方法的行参类型,如 String 等,设置为 .. 表示允许零个或多个参数。

2. 切点分类

根据作用对象切点共分为下列几类,下面分别介绍各类定义方式

(1) 方法切点

execution 作用域细化到方式,也是最常用的方式,具体参数参考上一点的表格。

如下切点定义示例作用域为 xyz.ibudai.controller 包中所有类的方法。

@Pointcut("execution (public * xyz.ibudai.controller.*.*(..))")
public void pointcut() {
}
(2) 类切点

within 作用域细化到类,即只能监听类中所有方法,无法细化到指定方法。

如下示例定义了监控 StudentServiceImpl 类中所有方法。

@Pointcut("within(xyz.ibudai.service.Impl.StudentServiceImpl)")
public void pointcut() {
}
(3) 参数切点

args() 用于定义参数切面,其参数说明如下:

  • args(): 不带参数。
  • args(..): 任意参数。
  • args(java.lang.String): 指定参数类型。
@Pointcut("args(..)")
public void pointcut() {
}
(4) 注解切点

通过 @annotation 定义注解切面,提供两种如下两种方式,第二种方式可以获取注解中的值更为常用。

@Pointcut("@annotation(xyz.ibudai.anntation.MethodAnn)")
public void pointcut1() {
}

@Pointcut("@annotation(methodAnn) && @annotation(xyz.ibudai.anntation.MethodAnn)")
public void pointcut2() {
}

@Around("pointcut2()")
public Object around2(ProceedingJoinPoint joinPoint, MethodAnn methodAnn) {
    System.out.println("value 2: " + methodAnn.value());
}

3. 通知事件

通知是切面在连接点处执行的代码,可以在连接点之前、之后或周围执行,以实现不同的行为。

(1) @Before

前置通知在连接点之前执行。

@Before("pointcut()")
public void before(JoinPoint joinPoint) {
    System.out.println("Before advice: " + joinPoint);
}
(2) @Around

环绕通知在连接点前后都执行,可获取请求目标的详细信息并通过 joinPoint.proceed() 放行请求。

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {
    System.out.println("Around advice");
    // 作用类型
    logger.info("Pointcut effect type: " + joinPoint.getKind());
    // 作用域
    logger.info("Pointcut effect range: " + joinPoint.toString());
    // 切面目标类
    logger.info("Pointcut target class: " + joinPoint.getTarget().getClass());
    // 切面方法
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    logger.info("Pointcut method name: " + signature.getMethod().getName());
    // 返回方法传入的参数
    Object[] obj = joinPoint.getArgs();
    logger.info("Pointcut receive parameter: " + Arrays.toString(obj));

    try {
        // 放行方法
        return joinPoint.proceed();
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
}
(3) @AfterThrowing

异常通知在连接点抛出异常时执行。

@AfterThrowing(value = "pointcut()", throwing = "throwable")
public void afterThrowing(JoinPoint joinPoint, Exception throwable) {
    logger.error("切面异常, 方法: {}", ((MethodSignature) joinPoint.getSignature()).getMethod().toString());
    System.out.println("After throwing " + joinPoint + ", throw message: " + throwable.getMessage());
}
(4) @AfterReturning

后置通知,在目标方法成功执行并返回结果后执行,即在方法返回之后执行。

@AfterReturning(value = "pointcut()", returning = "obj")
public void afterReturning(JoinPoint joinPoint, Object obj) {
    System.out.println("After returning, " + joinPoint + ", return: " + obj);
}
(5) @After

后置通知,无论目标方法是正常返回还是抛出异常,都会在方法执行之后执行,@AfterReturning 可以理解为 @After 的真子集。

@After 使用方式与 @AfterReturning 相同,仅作用域不同,这里不再提供示例。


文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录