Skip to content

AOP

概念

连接点(Joinpoint)

  • 定义:程序执行过程中的一些特定点,如方法的调用或异常的抛出,这些点可以被拦截以增强功能。
  • 在Spring AOP中,连接点主要是指方法的调用。

切入点(Pointcut)

  • 定义:需要处理的连接点,通过切入点可以指定哪些连接点需要被增强。
  • 切入点是一个描述信息,它修饰的是连接点,通过切入点表达式可以确定哪些连接点需要被拦截。

切面(Aspect)

  • 定义:封装了横切关注点的类,是切入点和通知(增强)的结合。
  • 切面包含了要执行的横切逻辑,以及这些逻辑应该被应用到哪些连接点上。

通知(Advice)

  • 定义:也被称为增强,是由切面添加到特定的连接点的一段代码。
  • 通知指定了在连接点被拦截后要执行的操作。
  • 通知类型包括前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)和环绕通知(Around)。

引介(Introduction)

  • 定义:允许在现有的实现类中添加自定义的方法和属性。
  • 引介是一种特殊的增强,通过它可以为类动态地添加新的接口或方法实现。

目标对象(Target Object)

  • 定义:被通知(增强)的对象,即代理的目标对象。
  • 在没有AOP的情况下,目标对象需要实现所有业务逻辑。而在AOP的帮助下,目标对象可以只实现核心逻辑,而横切逻辑则通过AOP动态织入。

织入(Weaving)

  • 定义:将切面代码插入到目标对象上,从而生成代理对象的过程。
  • 织入可以在不同的时间点进行,包括编译期、类装载期和运行期。Spring AOP主要采用的是运行期织入。

代理(Proxy)

  • 定义:是通知应用到目标对象之后被动态创建的对象。
  • 代理对象包含了目标对象的所有方法,并在调用这些方法时应用相应的增强逻辑。

AOP编程

详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-spring-boot/demo-spring-boot-aop

编程步骤

步骤如下:

  • 导入AOP依赖
  • 编写切面Aspect
  • 编写通知方法
  • 指定切入点表达式
  • 测试AOP动态脂入

切入点表达式

https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html

切入点方法的JoinPointreturningthrowingProceedingJoinPoint参数

ProceedingJoinPoint 是 Around 通知的方法参数

java
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    try {
        this.invokedBefore = true;

        Object result = proceedingJoinPoint.proceed();

        this.invokedAfterReturning = true;

        return result;
    } catch (Throwable throwable) {
        this.invokedAfterThrowing = true;

        throw throwable;
    } finally {
        this.invokedAfter = true;
    }
}

JoinPoint 是 Before、AfterReturning、AfterThrowing、After 通知方法的参数

java
// 方法执行前
// * 表示匹配多个任意字符或者任意类型
// .. 表示匹配多个任意参数或者多个层级的包路径
@Before("pointcut()"/* 引用上面定义的pointcut切入点 */)
public void before(JoinPoint joinPoint/* joinpoint获取当前切入点信息 */) {
    this.methodName = getMethodName(joinPoint);
    Object[] argsObject = joinPoint.getArgs();
    this.args = new int[]{(int) argsObject[0], (int) argsObject[1]};
    this.invokedBefore = true;

    this.sharedStore.sharedList.add("aspect1");
}

// 方法执行后没有抛出异常
@AfterReturning(value = "pointcut()",
        returning = "result"/* 指定返回值的形参变量 */)
public void afterReturning(JoinPoint joinPoint, Object result) {
    this.methodName = getMethodName(joinPoint);
    this.result = result;
    this.invokedAfterReturning = true;
}

// 方法执行后抛出异常
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
    this.methodName = getMethodName(joinPoint);
    this.throwable = e;
    this.invokedAfterThrowing = true;
}

// 方法执行后无论是否抛出异常
@After("pointcut()")
public void after(JoinPoint joinPoint) {
    this.methodName = getMethodName(joinPoint);
    this.invokedAfter = true;
}

throwing 是 AfterThrowing 注解用于指定异常通知方法的 throwable 参数

java
// 方法执行后抛出异常
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
    this.methodName = getMethodName(joinPoint);
    this.throwable = e;
    this.invokedAfterThrowing = true;
}

returning 是 AfterReturning 注解用于指定返回通知方法的 return 参数

java
// 方法执行后没有抛出异常
@AfterReturning(value = "pointcut()",
        returning = "result"/* 指定返回值的形参变量 */)
public void afterReturning(JoinPoint joinPoint, Object result) {
    this.methodName = getMethodName(joinPoint);
    this.result = result;
    this.invokedAfterReturning = true;
}

多个切面的执行顺序@Order用法

java
// 定义切面的执行顺序,数值越小越先执行
@Order(1)
// 切面
@Component
@Aspect
public class MyAspect {
java
// 定义切面的执行顺序,数值越小越先执行
@Order(2)
@Component
@Aspect
public class AMyAspect {

@Around通知用法

java
package com.future.demo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AroundAspect {
    public boolean invokedBefore = false;
    public boolean invokedAfterReturning = false;
    public boolean invokedAfterThrowing = false;
    public boolean invokedAfter = false;

    @Pointcut("execution(int com.future.demo..MyCalculator.*(int, int))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        try {
            this.invokedBefore = true;

            Object result = proceedingJoinPoint.proceed();

            this.invokedAfterReturning = true;

            return result;
        } catch (Throwable throwable) {
            this.invokedAfterThrowing = true;

            throw throwable;
        } finally {
            this.invokedAfter = true;
        }
    }

    public void reset() {
        this.invokedBefore = false;
        this.invokedAfterReturning = false;
        this.invokedAfterThrowing = false;
        this.invokedAfter = false;
    }
}