切面(Aspect)横切关注点的模块化封装,通常是一个类,包含了通知(Advice)和切点(Pointcut)的定义。例如,“日志切面” 可以封装所有日志记录的逻辑。


通知(Advice)切面中具体的 “动作”,即横切逻辑的实现。根据执行时机不同,分为 5 种类型:
前置通知(Before):目标方法执行前执行。
后置通知(After):目标方法执行后执行(无论是否抛出异常)。
返回通知(AfterReturning):目标方法正常返回后执行。
异常通知(AfterThrowing):目标方法抛出异常后执行。
环绕通知(Around):包围目标方法执行,可在方法前后自定义逻辑,甚至控制方法是否执行。


切点(Pointcut)定义 “哪些方法需要被切面拦截”,通常通过表达式(如 AspectJ 表达式)指定。例如,“拦截所有 service 包下的方法”。


连接点(Join Point)程序执行过程中可被拦截的 “时机”,如方法调用、字段访问等。在 Spring AOP 中,连接点通常指方法的执行。

二、Spring AOP 的实现原理
Spring AOP 是 Java 中最主流的 AOP 实现,它基于动态代理

三、Spring AOP 的使用步骤
以 Spring Boot 环境为例,演示 AOP 的基本使用:
1. 引入依赖
在 pom.xml 中添加 Spring AOP 依赖(Spring Boot 已内置,无需额外版本号):
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

导入出问题,可以-u,强制更新快照版本(Snapshot)依赖;

2. 定义切面类
通过 @Aspect 注解标记切面类,并用 @Component 注册到 Spring 容器
---------------------------------
例子: 苍穹外卖中切面编程的应用(与普通的aop不同,使用了注解结合的方法):
一,首先定义了注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:update insert
    OperationType value(); //这里的OperationType是自己定义的类型
	//这里定义了方法,只需要在使用注解时传入方法对应的参数即可;
}
------------------------------
二,定义切面类(记得添加component和aspect注解): 

@Component
@Aspect
@Slf4j //用于生成日志
public class AutoFillAspect {

    /**
     * 切入点
     */
    /*execution中前面的*表示修饰符,例如int long;*/
    @Pointcut("execution(* com.sky.mapper.*.*(..) ) && @annotation(com.sky.annotation.AutoFill)")
    //表示只有mapper包下的类并且加入了AutoFill注解才会被识别
    public void autoFillPointCut() {
    }

    /**
     * 前置通知
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始公共字段自动填充数据");
        //1.获取当前操作的类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取当前拦截的方法签名
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解签名
        OperationType operationType = autoFill.value();//获取数据库的操作类型
        //2.获取当前拦截方法的参数-实体对象
        Object[] args = joinPoint.getArgs();
        Object entity = args[0];//实体对象,不确定是哪个对象,所以用object
        //3.准备赋值的数据
        LocalDateTime now = LocalDateTime.now();
        long currentId = BaseContext.getCurrentId();
        //4.根据当前操作的类型,为实体对象的公共字段赋值(反射)
        if (operationType == OperationType.INSERT) {
            try {
                Method setCreatTime = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreatUser = entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //赋值
                setCreatTime.invoke(entity, now);
                setCreatUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (operationType == OperationType.UPDATE) {

            try {
                Method setUpdateTime = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //赋值
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

-------------------------------------------------
三,具体使用类 (只需要在方法上添加注解并传入参数即可):
例如:
@AutoFill(value = OperationType.UPDATE) 

    void update(Dish dish);
	
	
	
---------------------------------------------------------------------------
aop中的切入点表达式:

二、常用切入点表达式类型及语法:

1. execution():匹配方法执行(最常用)

语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数类型) [throws 异常])

各部分说明:
修饰符:可选,如 public、private,省略时匹配任意修饰符(* 表示任意)。
返回值类型:必填,* 表示任意返回值;具体类型如 String、void 等。
包名。类名:必填,支持通配符:
*:匹配任意单个包 / 类(如 com.example.service.* 匹配 service 包下的所有类)。
..:匹配任意层级的子包或类(如 com.example..* 匹配 com.example 下所有子包的类)。
方法名:必填,* 表示任意方法;get* 表示所有以 get 开头的方法。
参数类型:必填,() 表示无参;(..) 表示任意参数(数量、类型不限);(String) 表示单个 String 参数;(String, int) 表示 String + int 参数。
throws 异常:可选,指定方法抛出的异常,省略时匹配任意异常


-------------------------------------------------------
连接点功能:

例子:
@Aspect
@Component
@Slf4j
public class AOP {

    @Pointcut("@annotation(com.xiangxiang.autoFill.AutoFill)")
    public void pointcut(){

    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint jointPoint) throws Throwable {
        //获取类名
        String className = jointPoint.getTarget().getClass().getName();
        //获取方法名
        String methodName = jointPoint.getSignature().getName();
        //获取参数
        Object[] args = jointPoint.getArgs();
        //获取返回值(只有around才有这个功能)
        Object result = jointPoint.proceed();

        return result;
    }
}