Skip to content

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 自定义注解实现通用的操作日志记录。当然,这只是一个简单的示例,实际项目中可能会更加复杂,需要根据具体需求进行扩展和优化。

Released under the MIT License.