<p>可用AOP切面在方法异常时自动捕获、分类并统计,需精准拦截目标包方法,用execution表达式限定范围,如execution(public com.example.biz.service...*(..)),结合@AfterThrowing提取异常类型、时间、类方法名等维度,异步聚合至ConcurrentHashMap并提供HTTP接口查看TopN排行,定时快照存Redis,通知内try-catch保障主流程,设内存上限防OOM,支持配置开关动态启停。</p>

可以用AOP切面在方法执行异常时自动捕获、分类并统计,关键在于精准拦截目标包的方法,并将异常信息聚合上报或持久化。
定义切点精准匹配指定包下的业务方法
使用 @Pointcut 表达式限定范围,推荐用 execution + 包路径 + 方法签名组合,例如只拦截 com.example.biz.service 下所有 public 方法:
-
execution(public * com.example.biz.service..*.*(..)) ——
..支持子包,*.*(..)匹配任意方法名和参数 - 避免过度拦截:不用
execution(* *.*(..))或模糊包名(如com.example..*),防止误伤框架或工具类 - 若需进一步过滤,可叠加 @annotation 条件,比如只统计加了
@BusinessOperation的方法
在异常通知中做轻量级统计与归类
用 @AfterThrowing 获取抛出的异常对象,结合方法签名提取关键维度:
- 记录异常类型(
throwable.getClass().getSimpleName())、完整类名、发生时间、所属类/方法名 - 按业务场景分组:比如将
SQLException归为「数据访问异常」,FeignException归为「远程调用异常」 - 避免在通知里做耗时操作(如写DB、发HTTP请求),建议异步推送到内存队列(如
ConcurrentLinkedQueue)或通过ApplicationEvent解耦
实现可落地的统计汇总机制
统计本身不等于日志堆砌,要支持快速查看趋势和定位高频问题:
- 用线程安全的
ConcurrentHashMap<string atomiclong></string>按「异常类型+方法签名」做计数,键形如"NullPointerException#OrderService.createOrder" - 提供简单 HTTP 接口(如
/actuator/exception-stats)暴露最近5分钟/1小时的 TopN 异常排行 - 配合定时任务(如每5分钟)把内存统计快照写入 Redis Hash 或本地文件,便于断电恢复或外部采集
补充健壮性设计
AOP 统计本身不能因异常处理失败而影响主流程:
- 在通知内部用 try-catch 包裹全部逻辑,吞掉统计过程中的任何异常(仅打 warn 日志)
- 设置内存统计上限(如最多存200个异常维度),超限时按 LRU 清理冷数据,防止 OOM
- 上线前用
@Profile("dev")隔离统计切面,生产环境通过配置开关(如app.exception-stats.enabled=true)动态启停

















