AI代码审查工具ai-review-pipeline:自动化代码质量保障实践
2026/5/2 17:03:05
AOP(面向切面编程)是 Spring 核心特性之一,Spring Boot 对 AOP 做了轻量化封装,无需复杂配置即可快速实现日志记录、性能监控、权限校验、事务管理等横切逻辑,大幅提升代码复用性和可维护性。
| 概念 | 说明 |
|---|---|
| 切面(Aspect) | 封装横切逻辑的类(如日志切面、权限切面),是 AOP 的核心载体 |
| 连接点(JoinPoint) | 程序执行过程中的任意节点(如方法调用、异常抛出),Spring 仅支持方法级连接点 |
| 切入点(Pointcut) | 匹配需要增强的连接点(如指定包下的所有方法),通过表达式精准筛选 |
| 通知(Advice) | 切面的具体增强逻辑,分为 5 种类型:✅ 前置通知(Before):方法执行前执行✅ 后置通知(After):方法执行后执行(无论是否异常)✅ 返回通知(AfterReturning):方法正常返回后执行✅ 异常通知(AfterThrowing):方法抛出异常后执行✅ 环绕通知(Around):包裹方法执行,可自定义执行时机 |
| 织入(Weaving) | 将切面逻辑融入目标方法的过程,Spring Boot 中默认是运行时织入 |
xml
<!-- Spring Boot AOP 核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>java
运行
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; // 1. 标记为切面类 + 交给 Spring 管理 @Aspect @Component public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); // 2. 定义切入点:匹配 com.example.demo.service 包下所有类的所有方法 @Pointcut("execution(* com.example.demo.service.*.*(..))") public void servicePointcut() {} // 3. 前置通知:记录方法调用信息 @Before("servicePointcut()") public void beforeMethod(JoinPoint joinPoint) { String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); log.info("【前置通知】调用方法:{}.{},参数:{}", className, methodName, args); } // 4. 环绕通知:监控方法执行耗时 @Around("servicePointcut()") public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); // 执行目标方法 Object result = joinPoint.proceed(); long costTime = System.currentTimeMillis() - startTime; log.info("【环绕通知】方法 {} 执行耗时:{}ms", joinPoint.getSignature().getName(), costTime); return result; } // 5. 返回通知:记录方法返回值 @AfterReturning(value = "servicePointcut()", returning = "result") public void afterReturningMethod(JoinPoint joinPoint, Object result) { log.info("【返回通知】方法 {} 返回结果:{}", joinPoint.getSignature().getName(), result); } // 6. 异常通知:记录方法异常信息 @AfterThrowing(value = "servicePointcut()", throwing = "e") public void afterThrowingMethod(JoinPoint joinPoint, Exception e) { log.error("【异常通知】方法 {} 抛出异常:{}", joinPoint.getSignature().getName(), e.getMessage(), e); } }java
运行
import org.springframework.stereotype.Service; @Service public class UserService { public String getUserInfo(Long id) { // 模拟业务逻辑 if (id <= 0) { throw new IllegalArgumentException("用户ID不能为负数"); } return "用户ID:" + id + ",姓名:张三"; } }调用getUserInfo(1L)会输出:
plaintext
【前置通知】调用方法:com.example.demo.service.UserService.getUserInfo,参数:[1] 【环绕通知】方法 getUserInfo 执行耗时:2ms 【返回通知】方法 getUserInfo 返回结果:用户ID:1,姓名:张三调用getUserInfo(-1L)会输出:
plaintext
【前置通知】调用方法:com.example.demo.service.UserService.getUserInfo,参数:[-1] 【异常通知】方法 getUserInfo 抛出异常:用户ID不能为负数| 表达式类型 | 示例 | 说明 |
|---|---|---|
| 按方法签名匹配 | execution(* com.example.service.*.*(..)) | 匹配 service 包下所有类的所有方法 |
| 按注解匹配 | @annotation(com.example.annotation.Log) | 匹配标注了 @Log 注解的方法 |
| 按包匹配 | within(com.example.service..*) | 匹配 service 包及子包下的所有类 |
java
运行
// 自定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String desc() default ""; // 日志描述 } // 切面匹配注解 @Pointcut("@annotation(com.example.demo.annotation.Log)") public void annotationPointcut() {} // 使用注解 @Service public class OrderService { @Log(desc = "创建订单") public String createOrder(String orderNo) { return "订单创建成功:" + orderNo; } }@EnableAspectJAutoProxy(proxyTargetClass = true)强制使用 CGLIB。A.method1()调用A.method2()),method2的切面不会生效(代理对象未被触发),可通过注入自身 Bean 解决。@Order(n)指定执行顺序,n 越小优先级越高。@Transactional(底层基于 AOP)控制事务边界。