Spring Boot AOP 的核心概念

AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,用于将横切关注点(如日志、事务、权限校验)与业务逻辑解耦。通过动态代理技术,Spring Boot AOP 可以在不修改源代码的情况下,在方法执行前后插入通用逻辑。


AOP 的五大核心应用场景

场景 说明 典型实现
日志记录 统一记录方法入参、返回值、异常信息 @Around 环绕通知
事务管理 声明式事务(如 @Transactional Spring 内置事务切面
权限校验 拦截请求,验证用户角色或权限 @Before 前置通知 + 自定义注解
性能监控 统计方法执行耗时 @Around + 计时逻辑
缓存处理 方法执行前检查缓存,存在则直接返回 @Around + Redis 操作
异常统一处理 全局捕获异常并返回标准化错误响应 @AfterThrowing 异常通知

Spring Boot AOP 实现步骤

1. 添加依赖

pom.xml 中引入 AOP 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建切面类

使用 @Aspect@Component 定义切面:

package com.example.spring_demo01.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    // 定义切点:拦截所有 Service 层方法
    @Pointcut("execution(* com.example.spring_demo01.service.*.*(..))")
    public void serviceLayer() {}

    // 环绕通知:记录方法耗时
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 执行目标方法
        long duration = System.currentTimeMillis() - startTime;
        System.out.printf("方法 %s 执行耗时: %d ms\n", joinPoint.getSignature(), duration);
        return result;
    }
}
3. 定义自定义注解(可选)

创建注解用于标记需要特殊处理的方法:

package com.example.spring_demo01.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {
}
4. 实现权限校验切面
@Aspect
@Component
public class AuthAspect {

    // 拦截所有标记了 @AdminOnly 的方法
    @Before("@annotation(com.example.spring_demo01.annotation.AdminOnly)")
    public void checkAdminPermission() {
        // 模拟权限校验
        if (!CurrentUser.isAdmin()) {
            throw new RuntimeException("无权限访问!");
        }
    }
}
5. 在业务方法上使用注解
@Service
public class UserService {

    @AdminOnly
    public void deleteUser(Long userId) {
        // 删除用户逻辑
    }
}

AOP 核心注解详解

注解 说明
@Aspect 声明一个切面类
@Pointcut 定义切点表达式,确定哪些方法需要被拦截
@Before 在目标方法执行前执行
@After 在目标方法执行后执行(无论是否抛出异常)
@AfterReturning 在目标方法成功返回后执行
@AfterThrowing 在目标方法抛出异常后执行
@Around 环绕通知,可完全控制目标方法的执行

切点表达式语法

表达式示例 说明
execution(* com.example.service.*.*(..)) 匹配 service 包下所有类的所有方法
@annotation(com.example.AdminOnly) 匹配标记了 @AdminOnly 注解的方法
within(com.example.controller..*) 匹配 controller 包及其子包下的所有方法
args(java.lang.String, ..) 匹配第一个参数为 String 类型的方法

AOP 实现原理

Spring AOP 基于 动态代理 实现:

  • JDK 动态代理:针对实现了接口的类,通过 Proxy 类生成代理对象。
  • CGLIB 代理:针对未实现接口的类,通过继承生成子类代理。

常见问题与解决方案

问题 1:AOP 不生效
  • 原因:同类内部方法调用(如 this.method())无法被代理拦截。
  • 解决:通过依赖注入调用自身方法:
    @Service
    public class UserService {
        @Autowired
        private UserService self; // 注入自身代理对象
    
        public void methodA() {
            self.methodB(); // 通过代理对象调用
        }
    
        @AdminOnly
        public void methodB() {}
    }
    
问题 2:切点表达式错误
  • 调试工具:使用 AopUtils 检查方法是否被代理:
    boolean isProxy = AopUtils.isAopProxy(bean);
    
问题 3:循环依赖
  • 原因:AOP 代理导致 Bean 初始化顺序冲突。
  • 解决:使用 @Lazy 延迟注入:
    @Service
    public class OrderService {
        @Lazy
        @Autowired
        private UserService userService;
    }
    

总结

通过 Spring Boot AOP,你可以高效地实现以下功能:

  1. 统一日志记录
  2. 声明式事务管理
  3. 细粒度权限控制
  4. 接口性能监控
  5. 全局异常处理

合理使用 AOP 能显著提升代码的可维护性和复用性,但需注意代理机制的限制(如内部方法调用问题)。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐