ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于注解 AOP 的 Service 层日志与异常统一处理:COLA CatchAndLog 组件实战

基于注解 AOP 的 Service 层日志与异常统一处理:COLA CatchAndLog 组件实战
  • 后端

【免费下载链接】COLA

🥤 COLA: Clean Object-oriented & Layered Architecture

项目地址:https://gitcode.com/gh_mirrors/col/COLA
点击查看免费下载

本文围绕 COLA 开源仓库中的 CatchAndLog(catch-log-starter)组件展开,讲解如何通过「注解 + AOP + Spring Boot 自动装配」为 Service 层方法提供统一的请求日志、响应日志与异常处理能力。读完本文,你将掌握该组件的底层原理(切点定义、环绕通知、异常分类与响应适配)、三步接入方法(加注解、开 DEBUG、看日志),以及如何借助仓库内置的 Starter 组件 Archetype 一键生成自己的 Spring Boot Starter。

组件定位:面向 Service 层的「日志 + 异常」一体化 Starter

组件原文档开门见山地给出了它的定位:

通过注解 AOP,提供 service 级别的 logging 和 exception 处理。

也就是说,它解决的是应用中最常见也最容易被忽视的两类横切问题:每个 Service 方法都要打印入参/出参/耗时日志,以及异常抛到上层前需要一个统一的处理出口(记录日志、转成约定的错误响应返回给调用方)。这两件事如果散落在每个方法里手写,代码会极其冗长且难以维护;而用注解 + AOP 收敛到切面里,业务代码可以保持干净。

在 COLA 仓库中,该组件对应两个形态:

  • 实际可用的组件:cola-component-catchlog-starter,坐标com.alibaba.cola:cola-component-catchlog-starter;
  • Archetype 模板:本文关联文档正是位于 cola-starter-component-archetype 模板中的 README,模板内嵌了一套最小可运行的 starter 示例代码(CatchAndLog 注解、CatchLogAspect 切面、CatchLogAutoConfiguration 自动配置、spring.factories 等),用于指导开发者照着实现自己的 Starter 组件。

一、原理:注解 + AOP 切面 + Spring Boot 自动装配

整个组件的原理可以从三个层面拆解:

1. 一个注解标记目标类

CatchAndLog.java 是一个运行时注解,可同时标注在类和方法上:

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface CatchAndLog { }

标注在类上时,表示该类下所有匹配的 public 方法都会被拦截(这也是文档中在GrouponServiceImpl类级别使用的原因);标注在方法上则可做到更细粒度的控制。

2. 一个切面做环绕拦截

CatchLogAspect.java 定义了切点与环绕通知:

@Pointcut("@within(CatchAndLog) && execution(public * *(..))") public void pointcut() { } @Around(value = "pointcut()") public Object around(ProceedingJoinPoint joinPoint) { long startTime = System.currentTimeMillis(); logRequest(joinPoint); Object response = null; try { response = joinPoint.proceed(); } catch (Throwable e) { response = handleException(joinPoint, e); } finally { logResponse(startTime, response); } return response; }

切点表达式@within(CatchAndLog) && execution(public * *(..))的含义是:凡是标注了@CatchAndLog的类型(@within)内部的 public 方法都会被拦截。环绕逻辑是标准的 AOP 套路:

  • 先记录请求(方法签名 + 入参,通过 fastjson 序列化);
  • 执行目标方法proceed();
  • 若抛出异常,交给handleException统一转换为返回值(详见下文「异常处理」);
  • 无论成功失败,在finally中记录响应与耗时。

Archetype 模板中的 CatchLogAspect.java 与仓库正式组件的实现思路完全一致,模板版本为了演示对异常处理保留了//Dummy implementation占位,正式组件则补充了完整的异常分级与响应转换逻辑。

3. 自动配置机制自动注入切面 Bean

依赖 Spring Boot 的 autoConfig 机制,组件无需任何手动配置即可生效。核心在 CatchLogAutoConfiguration.java:

@Configuration @EnableAspectJAutoProxy public class CatchLogAutoConfiguration { @Bean @ConditionalOnMissingBean(CatchLogAspect.class) public CatchLogAspect catchLogAspect() { return new CatchLogAspect(); } }

关键点有两个:

  • @EnableAspectJAutoProxy开启 AspectJ 自动代理,是切面生效的前提;
  • @ConditionalOnMissingBean(CatchLogAspect.class)允许使用者自定义自己的CatchLogAspectBean 来覆盖默认切面,实现按需定制。

自动配置类的注册入口在 Archetype 模板的 spring.factories 中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration = ${package}.CatchLogAutoConfiguration

即通过 Spring Boot 的spring.factories机制,将自动配置类登记到EnableAutoConfiguration列表中,应用启动时由 Spring Boot 自动加载(${package}是 Archetype 生成时被替换的占位符)。

二、快速接入:一行依赖即可生效

因为采用 autoConfig 机制,使用者只需添加依赖,连@EnableXXX开关注解都不需要:

<dependency> <groupId>com.alibaba.lst.tech.shared</groupId> <artifactId>catch-log-starter</artifactId> </dependency>

说明:上面的坐标是组件原文档中记载的阿里内部共享坐标;在 COLA 开源仓库中,对应的发布坐标是com.alibaba.cola:cola-component-catchlog-starter,见 cola-component-catchlog-starter/pom.xml。

从 pom.xml 可以看到组件底层依赖的构成,理解它有助于预判类路径冲突:

依赖作用scope
spring-boot-autoconfigure提供@ConditionalOnMissingBean等自动装配支持provided
spring-boot-starter-aop提供 AOP 运行时(AspectJ、代理)compile
com.alibaba:fastjson入参/出参的 JSON 序列化provided
org.slf4j:slf4j-api日志门面compile
cola-component-dto提供Response等返回值类型compile
cola-component-exception提供BizException/SysException异常体系compile

三、使用步骤:三步完成接入

步骤 1:在 Service 类上添加 @CatchAndLog 注解

@CatchAndLog public class GrouponServiceImpl implements GrouponService { // 类的所有 public 方法自动获得日志 + 异常处理能力 }

步骤 2:为组件开启 DEBUG 级日志输出

CatchLogAspect 的日志全部打在DEBUG级别(详见下文日志行说明),因此默认日志配置下是看不到的。在logback-test.xml(或项目自己的 logback 配置)中加入:

<!--这个是统一异常处理,日志记录组件的日志--> <logger name="com.alibaba.cola.catchlog" level="DEBUG"/>

Archetype 模板自带的 logback-test.xml 就是标准做法:root 保持 INFO,仅将组件包名(模板中为${package},即生成工程自己的包)设为 DEBUG。生产环境建议同样只对需要排查的包开 DEBUG,避免全量 DEBUG 刷屏。

步骤 3:通过控制台日志验证 AOP 拦截已生效

文档给出了预期的控制台输出:

DEBUG c.a.l.t.s.catchlog.CatchLogAspect - Start processing: GrouponServiceImpl.queryGrouponItemDetail(..) DEBUG c.a.l.t.s.catchlog.CatchLogAspect - REQUEST : 257 DEBUG c.a.l.t.s.catchlog.CatchLogAspect - RESPONSE : {"errCode":"UNKNOWN_ERROR"...} DEBUG c.a.l.t.s.catchlog.CatchLogAspect - COST : 1329ms

各行的业务含义分别是:被拦截的方法签名、序列化后的请求入参、序列化后的响应体、方法执行耗时(毫秒)。只要看到这四类日志,就说明 CatchAndLog 已经在做 AOP 拦截。

对照当前仓库源码 CatchLogAspect.java,实际输出的日志格式与文档示例略有差异(新版本统一为大写前缀并改用 SLF4J 占位符):

START PROCESSING: GrouponServiceImpl.queryGrouponItemDetail(..) REQUEST: {"id":257} RESPONSE: {"success":false,"errCode":"UNKNOWN_ERROR",...} COST: 1329ms

另外两点值得注意的实现细节:

  • 入参序列化使用了SerializerFeature.IgnoreErrorGetter,遇到 getter 抛异常的字段不会拖垮整个日志;
  • 序列化、日志记录本身都包裹在 try-catch 中并「吞掉」异常,保证日志逻辑永远不影响业务主流程。

四、异常处理的统一出口:ResponseHandler 机制

文档特别强调组件「兼容普通的 HSF service 和 Mtop service」,并给出了ResponseHandler的设计示意:

public class ResponseHandler { public static Object handle(Class returnType, String errCode, String errMsg){ if (isColaResponse(returnType)){ return handleColaResponse(returnType, errCode, errMsg); } if(isMtopResponse(returnType)){ return handleMtopResponse(returnType, errCode, errMsg); } return null; } ... }

其核心思想是:异常被捕获后,要根据方法的返回类型,构造一个「携带错误码与错误信息的响应对象」返回给调用方,而不是让异常直接冒泡。这样不同的服务协议(COLA Response、Mtop Response 等)都能获得与自己约定格式一致的错误结果。

当前仓库的实现形态

开源仓库将这一设计落地为三个协作类:

  • ResponseHandlerI.java:处理器的统一接口;
  • DefaultResponseHandler.java:默认实现,当前内置了COLA Response 分支——当方法返回类型是Response或其子类(returnType == Response.class || returnType.getGenericSuperclass() == Response.class)时,通过反射newInstance()构造响应对象,填充success=false、errCode、errMessage后返回;文档中的 Mtop 分支为原 README 展示的扩展方向,开源默认实现未内置;
  • ResponseHandlerFactory.java:工厂类,优先从 Spring 容器中取自定义的ResponseHandlerIBean,取不到才回退到DefaultResponseHandler。

也就是说,你完全可以像测试用例 CustomResponseHandler.java 那样,实现ResponseHandlerI并注册为 Spring Bean,即可替换默认的错误响应构造逻辑——这正是「兼容多种 service 协议」的扩展点所在。

异常分级:BizException 与 SysException

切面中 handleException 对异常做了三档分类,对应 COLA 的异常体系(cola-component-exception):

异常类型日志级别语义默认错误码
BizExceptionWARN(DEBUG 时打印堆栈)已知业务异常,无需重试BIZ_ERROR
SysExceptionERROR(含堆栈)系统异常,重试可能成功SYS_ERROR
其他ThrowableERROR(含堆栈)未知异常UNKNOWN_ERROR

三种情况最终都会交给ResponseHandlerFactory.get().handle(returnType, errCode, errMessage)构造带错误码的响应返回。文档日志示例中的{"errCode":"UNKNOWN_ERROR"...}正是「未知异常 + 响应转换」两条链路共同作用的结果。

五、进阶:用 Archetype 一键生成自己的 Starter 组件

文档所在的 cola-starter-component-archetype 是 COLA 为「快速创建 components 工程」提供的 Maven Archetype 模板(dev-util-archetypes 说明),模板内嵌了本文前面讲到的全套最小实现:注解、切面、自动配置、spring.factories、测试配置与启动类。利用它,可以把这个「日志 + 异常」组件的骨架原样复刻到自己的组件中。

archetype-metadata.xml 声明了生成的内容:src/main/java下的 Java 源码(按包名 packaged 并过滤占位符)、src/main/resources下的 spring.factories、src/test下的测试工程。生成时package、artifactId等占位符会被替换为用户指定值。

仓库还提供了封装脚本 new-cola-starter-component.sh,核心命令逻辑等价于:

mvn archetype:generate \ -DgroupId=<组件组名> \ -DartifactId="cola-component-<name>-starter" \ -Dpackage="com.alibaba.cola.<name>" \ -DarchetypeGroupId=<archetype groupId> \ -DarchetypeArtifactId=cola-starter-component-archetype \ -DarchetypeVersion=<archetype version> \ -DinteractiveMode=false \ -DarchetypeCatalog=local

执行后即得到一个与cola-component-catchlog-starter同构的新 Starter 工程:CatchAndLog注解 +CatchLogAspect切面 +CatchLogAutoConfiguration自动配置 +spring.factories注册入口,稍加改造即可变成你自己的横切组件。

六、测试验证:多场景覆盖

组件的正确性由测试用例完整覆盖。CatchLogTest.java 中的用例场景包括:

  • 普通方法调用(testAspect);
  • 带请求参数调用(testCatchAndLog);
  • 返回Response类型(testExecuteWithResponse);
  • 返回 void(testExecuteWithVoid);
  • 抛出BizException且返回Response(testExecuteWithBizExceptionAndResponse);
  • 抛出SysException且返回Response(testExecuteWithSysExceptionAndResponse);
  • 未知RuntimeException与 void/Response 组合。

这些场景由 Demo.java 提供(一个标注了@CatchAndLog的演示 Bean),覆盖了「成功、业务异常、系统异常、未知异常」与「void、COLA Response」的交叉组合,正好验证了前文讲到的异常分级与按返回类型适配响应的行为。

小结

CatchAndLog 组件用最少的注解侵入,把 Service 层普遍存在的日志记录与异常处理收敛到一处:@CatchAndLog负责标记、CatchLogAspect负责拦截记录、ResponseHandlerI负责按返回类型转换错误响应、BizException/SysException负责异常分级,再配合 Spring Boot autoConfig 做到零配置接入。如果在自己的项目中遇到了「每个 Service 方法都在重复打日志、重复 catch 异常」的痛点,参照本文的接入三步与 cola-component-catchlog-starter 源码,即可快速落地一套统一的 Service 层横切能力。

  • 后端

【免费下载链接】COLA

🥤 COLA: Clean Object-oriented & Layered Architecture

项目地址:https://gitcode.com/gh_mirrors/col/COLA
点击查看免费下载

相关推荐

上一篇:革命性跨平台Flutter应用:LoveIwara重新定义二次元内容消费体验
下一篇:VGGT模型微调指南:迁移学习在新场景中的应用

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进