题目
使用Spring AOP实现方法执行日志记录
信息
- 类型:问答
- 难度:⭐
考点
AOP概念,注解配置,切点表达式
快速回答
使用Spring AOP实现方法日志记录的步骤如下:
- 添加Spring AOP依赖(spring-boot-starter-aop)
- 创建切面类并用
@Aspect和@Component注解标记 - 使用
@Before或@Around注解定义通知 - 通过切点表达式指定目标方法(如
@Pointcut("execution(* com.example.service.*.*(..))")) - 在通知方法中实现日志逻辑
原理说明
Spring AOP通过动态代理在目标方法执行前后插入横切逻辑(如日志记录)。核心组件:
- 切面(Aspect):封装横切关注点的模块(如日志切面)
- 切点(Pointcut):定义哪些方法需要被增强
- 通知(Advice):增强的具体实现(如@Before)
代码示例
// 1. 添加Maven依赖
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-aop</artifactId>
// </dependency>
// 2. 定义切面
@Aspect
@Component
public class LoggingAspect {
// 3. 定义切点(匹配service包下所有方法)
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 4. 前置通知
@Before("serviceMethods()")
public void logMethodCall(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("执行方法: " + methodName);
}
// 5. 环绕通知(带返回值)
@Around("serviceMethods()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long duration = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " 执行耗时: " + duration + "ms");
return result;
}
}最佳实践
- 使用
@Around处理耗时统计等需要控制方法执行的场景 - 优先使用精确的切点表达式(如
execution(* com.example.service.UserService.*(..)))避免意外匹配 - 在通知方法中通过
JoinPoint获取方法签名、参数等信息
常见错误
- 切面未生效:忘记添加
@EnableAspectJAutoProxy(Spring Boot中自动配置) - 表达式错误:如
execution(* *Service(..))漏写通配符导致匹配失败 - 循环代理:切面方法调用自身导致栈溢出
扩展知识
- 通知类型:
@Before:方法执行前@AfterReturning:方法成功返回后@AfterThrowing:方法抛出异常后@After:方法结束后(finally块)
- 代理机制:Spring AOP默认使用JDK动态代理(需接口),若无接口则用CGLIB
- 执行顺序:多个切面可通过
@Order注解指定优先级