题目
设计一个Spring AOP切面监控Service方法执行时间并预警
信息
- 类型:问答
- 难度:⭐⭐
考点
AOP切面定义,环绕通知使用,条件日志记录,性能监控
快速回答
实现步骤:
- 创建
@Aspect组件定义切面 - 使用
@Around环绕通知捕获方法执行时间 - 通过
ProceedingJoinPoint控制目标方法执行 - 计算耗时并与阈值比较
- 使用SLF4J按条件记录日志
关键配置:
- 切入点表达式:
@Pointcut("execution(* com.example.service.*.*(..))") - 日志分级:
logger.warn()超时警告
一、实现原理
通过Spring AOP的环绕通知(Around Advice)在目标方法执行前后插入计时逻辑:
- 调用
System.currentTimeMillis()记录开始时间 - 通过
ProceedingJoinPoint.proceed()执行目标方法 - 计算耗时并判断是否超过阈值
- 使用日志框架分级输出(INFO记录正常,WARN记录超时)
二、代码示例
@Aspect
@Component
public class PerformanceMonitorAspect {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorAspect.class);
private static final long THRESHOLD = 1000; // 阈值1秒
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Around("serviceMethods()")
public Object monitorTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long duration = System.currentTimeMillis() - startTime;
if (duration > THRESHOLD) {
logger.warn("方法执行超时 - {}: {} ms",
joinPoint.getSignature(), duration);
} else {
logger.info("方法执行完成 - {}: {} ms",
joinPoint.getSignature(), duration);
}
return result;
}
}三、最佳实践
- 切入点优化:使用
@annotation自定义注解标记需要监控的方法,避免全量扫描 - 阈值可配置化:通过
@Value从配置文件中读取阈值 - 异步日志:使用AsyncAppender避免日志写入影响性能
- 异常处理:在finally块中确保耗时计算不会因异常中断
四、常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
| 切入点表达式错误 | 切面未生效 | 使用AspectJ Weaver验证表达式 |
| 未调用proceed() | 目标方法未执行 | 确保环绕通知内调用joinPoint.proceed() |
| 在切面内抛异常 | 业务逻辑中断 | 用try-catch处理异常并记录 |
| 频繁记录低耗时日志 | 日志爆炸/性能下降 | 仅记录超时请求,或采用采样日志 |
五、扩展知识
- 动态阈值调整:集成Spring Cloud Config实现运行时阈值热更新
- 监控集成:将耗时数据推送到Prometheus+Grafana实现可视化监控
- 链路追踪:结合Sleuth+Zipkin记录跨服务调用链耗时
- AOP代理机制:理解JDK动态代理与CGLIB代理的区别(Spring Boot 2.x默认使用CGLIB)
六、配置说明
在Spring Boot启动类添加@EnableAspectJAutoProxy(2.x版本默认开启):
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}