Spring AOP

深入理解 Spring AOP:原理、应用与实战

Spring AOP(面向切面编程)是 Spring 框架的重要模块之一,用于处理日志记录、权限控制、事务管理等通用功能的横切关注点。

本文将从 AOP 的核心概念入手,结合底层原理与代码示例,带你深入掌握 Spring AOP 的强大能力。

一、什么是 AOP?

AOP(Aspect-Oriented Programming)是一种编程范式,用于将横切关注点(如日志、安全、事务)从业务逻辑中分离出来。

横切关注点 vs 纵向业务逻辑

传统开发中,日志、权限、事务控制会散落在每个业务方法中,增加了代码耦合与重复。

AOP 将这些逻辑抽取成 "切面",独立维护,实现"关注点分离"。

二、Spring AOP 的实现方式

Spring AOP 本质是基于动态代理实现的,分为两种方式:

  1. 基于 JDK 动态代理:代理接口
  2. 基于 CGLIB 字节码增强:代理类

Spring 自动判断:

  • 如果目标类实现接口,则使用 JDK 动态代理
  • 否则使用 CGLIB

三、核心概念

概念 含义
JoinPoint 程序执行的点,例如方法调用
Pointcut 匹配 JoinPoint 的规则
Advice 通知,切面逻辑
Aspect 切面,包含切点与通知
Weaving 将切面应用到目标对象的过程

四、注解方式使用 AOP

1. 添加依赖(Spring Boot 已内置)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 编写切面类

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void logPointcut() {}

    @Before("logPointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("方法执行前: " + joinPoint.getSignature());
    }

    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("方法执行后: 返回结果 = " + result);
    }
}

3. 业务类

@Service
public class UserService {
    public String getUser(String id) {
        return "用户:" + id;
    }
}

4. 启动类开启 AOP

@SpringBootApplication
@EnableAspectJAutoProxy
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

运行后,调用 getUser() 会自动触发切面逻辑。

五、自定义注解 + AOP 实战

1. 创建自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String value() default "操作日志";
}

2. 使用注解标记方法

@Service
public class OrderService {
    @MyLog("下单操作")
    public void createOrder(String userId) {
        System.out.println("执行下单逻辑");
    }
}

3. 切面实现拦截注解

@Aspect
@Component
public class MyLogAspect {

    @Around("@annotation(myLog)")
    public Object around(ProceedingJoinPoint pjp, MyLog myLog) throws Throwable {
        System.out.println("日志开始 - " + myLog.value());
        Object result = pjp.proceed();
        System.out.println("日志结束");
        return result;
    }
}

六、Spring AOP 底层原理解析

1. 注册切面类

在 Spring 启动阶段,@Aspect 类被扫描注册为 Bean,并识别其切面方法。

2. Bean 创建阶段

AbstractAutoProxyCreator#postProcessAfterInitialization 中,对满足条件的 Bean 生成代理对象。

3. 调用代理对象

代理对象内部维护了一个通知链,当执行目标方法时,会触发所有相关 Advice。

// 简化版逻辑:
public Object invoke(MethodInvocation invocation) throws Throwable {
    for (Advice advice : adviceChain) {
        advice.before(...);
    }
    Object result = invocation.proceed();
    for (Advice advice : adviceChain) {
        advice.after(...);
    }
    return result;
}

七、AOP 使用建议与注意事项

  • 避免对构造函数、final 方法使用 AOP(JDK 动态代理不支持)
  • 不建议在同一类中自调用代理方法,AOP 不生效(因为绕过了代理)
  • 推荐使用注解 + AOP 结合日志、权限校验、缓存、限流等通用能力

八、总结

Spring AOP 是对横切逻辑解耦的优雅方案,广泛应用于日志、权限、事务等领域。

  • 本质基于动态代理实现
  • 通过切面定义 Pointcut 与 Advice
  • 支持注解、XML、多种方式配置