题目
实现带条件过滤的Service层方法执行时间监控
信息
- 类型:问答
- 难度:⭐⭐
考点
Spring AOP切面定义,切入点表达式,环绕通知实现,性能优化,日志记录
快速回答
要实现带条件过滤的执行时间监控切面,需要:
- 使用
@Around环绕通知结合ProceedingJoinPoint - 定义精确的切入点表达式定位Service层方法
- 在通知中计算执行时间并添加阈值过滤条件
- 使用Slf4j记录WARN级别日志
- 通过方法签名提取避免反射性能损耗
原理说明
Spring AOP基于代理模式实现,通过切入点表达式匹配连接点,环绕通知可以完全控制目标方法的执行。本题需要:1) 精确拦截Service层方法;2) 计算执行时间;3) 添加阈值过滤;4) 高效记录日志。关键在于切入点表达式的精确性和环绕通知的性能优化。
代码示例
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Aspect
@Component
public class PerformanceMonitoringAspect {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitoringAspect.class);
private static final long THRESHOLD_MS = 100;
// 精确匹配service包下所有类的public方法
@Around("execution(public * com.example.service..*.*(..))")
public Object monitorMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 使用StopWatch替代System.currentTimeMillis()提高精度
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed(); // 执行目标方法
stopWatch.stop();
long executionTime = stopWatch.getTotalTimeMillis();
if (executionTime > THRESHOLD_MS) {
// 直接获取方法签名避免反射调用
String methodSignature = joinPoint.getSignature().toLongString();
logger.warn("方法执行超时 - 方法: {}, 耗时: {}ms, 调用时间: {}",
methodSignature, executionTime, new java.util.Date());
}
return result;
}
}最佳实践
- 切入点优化:使用
execution(public * com.example.service..*.*(..))精确匹配service包下的public方法 - 性能关键点:
- 使用
StopWatch替代System.currentTimeMillis()提高时间测量精度 - 通过
joinPoint.getSignature()直接获取方法签名,避免反射调用 - 阈值判断在日志记录前,避免不必要的字符串拼接
- 使用
- 日志规范:使用参数化日志语句
logger.warn("方法: {}, 耗时: {}ms", ...)避免字符串拼接开销
常见错误
- 切入点过宽:如使用
*.*(..)会拦截所有Bean导致性能问题和误报 - 反射滥用:通过
joinPoint.getTarget().getClass()获取类信息会产生额外开销 - 未考虑异常情况:若目标方法抛出异常,会跳过时间记录导致数据不完整(应在finally块中停止计时)
- 日志级别误用:使用
logger.debug()但未检查日志级别开启状态,仍会执行参数构造
扩展知识
- 条件切入点:可通过
@annotation实现基于自定义注解的监控,如@Around("@annotation(MonitorPerformance)") - 动态阈值:使用
@Value("${monitor.threshold:100}")从配置中心动态获取阈值 - 异步记录:对于耗时日志操作,可结合
@Async将日志记录放入线程池执行 - 监控指标集成:可将数据推送到Micrometer,与Prometheus/Grafana集成实现可视化监控
- 编译时织入:对于性能敏感场景,考虑使用AspectJ编译时织入替代Spring AOP运行时代理