Spring Boot 基于 AOP 自定义注解实现通用功能
在我们系统开发,除了常规的系统日志会把日志通过 ELK 组件完成,经常也需要关注具体某一类的业务执行情况,比如调用第三系统的情况,采集到专门的日志系统好进行统一,可以更加直观的看到异常的原因。基于此,我们会开发自定义注解组件。
本文,我们会简化这个步骤, 不会完整的把采集数据,发送到 kafka,然后消费消息入库这个整条链路实现,只实现自定义注解的核心逻辑
自定义注解
接下来,我们需要创建一个自定义注解 @Log,用于标注需要进行日志记录的方法
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
// 业务类型
String value() default "";
}
创建切面类
然后,我们需要创建一个切面类 LogAspect,用于拦截带有 @Log 注解的方法,并进行日志记录。
java
@Slf4j
@Aspect
@Component
public class LogAspect {
private final ObjectMapper objectMapper = new ObjectMapper();
@Value("${spring.application.name}")
private String applicationName;
// 定义切点,拦截所有带有 @Log 注解的方法
@Pointcut("@annotation(com.ssn.boot.annotation.log.Log)")
public void logPoint(){}
// 环绕通知,记录方法执行前后的日志
@Around("logPoint()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
LogEntity logEntity = new LogEntity();
try {
long enterTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Log annotation = method.getAnnotation(Log.class);
String value = annotation.value();
if (!StringUtils.hasLength(value)) {
value = applicationName;
}
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
logEntity.setClassName(className);
logEntity.setMethodName(methodName);
logEntity.setBusinessName(value);
logEntity.setEnterTime(enterTime);
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
String params = objectMapper.writeValueAsString(args);
logEntity.setArgs(params);
}
Object result = joinPoint.proceed();
if (result != null) {
logEntity.setResult(objectMapper.writeValueAsString(result));
}
return result;
} catch (Exception e) {
logEntity.setError(e.getMessage());
throw e;
} finally {
long exitTime = System.currentTimeMillis();
logEntity.setExitTime(exitTime);
log.info("执行日志 {}",logEntity);
// TODO: 实现日志采集功能,例如发送到 Kafka 或存储到数据库
}
}
}
测试
在相关需要记录日志的方法标记 @Log 日志注解, 执行的时候就可以看到相关日志输出
执行日志 LogEntity(businessName=文本渲染, className=com.ssn.boot.application.text.TextRenderService, methodName=render, args=["aa","需要渲染的文本","groupA"], enterTime=1735883571291, exitTime=1735883571304, result=null, error=Unsuport)
总结
通过以上步骤,我们就可以使用 SpringBoot 自定义注解实现通用的操作日志记录。当然,这只是一个简单的示例,实际项目中可能会更加复杂,需要根据具体需求进行扩展和优化。