【切面编程】自定义注解实现操作日志

创建一个项目工程

引入相关依赖

<!-- aop切面 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- lombok -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>

新建一个自定义注解

/**
 * @date 2024/7/31 20:09
 * @description 日志注解
 */
@Target({ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {

    /**
     * 模块名称
     *
     * @return
     */
    String title() default "";

    /**
     * 业务类型
     *
     * @return
     */
    String businessType() default "";
}

创建相关的切面类

package com.demo.demos.web.aspect;

import com.demo.demos.web.annotation.Log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * @date 2024/7/31 20:12
 * @description 自定义日志注解切面类
 */
@Slf4j
@Aspect
@Component
public class LogAspect {


    /**
     * 定义切点表达式,用于识别需要进行日志记录的方法。
     * 该切点表达式指定了标注有特定注解的方法,即使用了com.demo.demos.web.annotation.Log注解的方法。
     * 这样做的目的是为了在这些方法执行前后插入日志记录的逻辑,而不需要直接修改这些方法的代码。
     */
    private static final String POINT_CUT = "@annotation(com.demo.demos.web.annotation.Log)";

    /**
     * 定义一个切点方法,无参数无返回值。
     * 该方法的存在是为了配合切点表达式使用,切点表达式定义了哪些方法应该被这个切点方法影响。
     * 在这个例子中,@Pointcut注解将log()方法与POINT_CUT定义的切点表达式关联起来,
     * 使得任何符合切点表达式的方法都会受到通知(Advice)的影响,
     * 这里的通知可能是记录日志、性能监控等切面逻辑。
     */
    @Pointcut(POINT_CUT)
    private void log() {

    }

    /**
     * 定义一个环绕通知方法,用于在目标方法执行前后进行日志记录。
     * 该方法接收一个ProceedingJoinPoint类型的参数,用于获取目标方法的相关信息,
     * 并在目标方法执行前后进行日志记录。
     *
     * @param joinPoint ProceedingJoinPoint类型的参数,用于获取目标方法的相关信息
     * @return 目标方法的返回值
     * @throws Throwable 目标方法抛出的异常
     */
    @Around("log()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 请求开始时间
        long startTime = System.currentTimeMillis();
        log.info("请求开始时间:{}", startTime);

        // 获取注解所在方法上的相关参数
        Object[] args = joinPoint.getArgs();
        log.info("请求参数:{}", args);

        Log apiLog = getApiOperationLogDescription(joinPoint);
        if (apiLog != null) {
            log.info("请求标题:{}", apiLog.title());
            log.info("请求类型:{}", apiLog.businessType());
        }


        Object result = joinPoint.proceed();
        log.info("请求返回值:{}", result);

        // 执行耗时
        long executionTime = System.currentTimeMillis() - startTime;
        log.info("请求执行耗时:{}", executionTime);

        return result;
    }


    /**
     * 获取方法上的日志注解实例。
     * 该方法旨在通过反射机制,从给定的 ProceedingJoinPoint 中提取目标方法的信息,
     * 并进一步获取该方法上是否标注了 Log 注解,从而为后续的日志记录提供必要的配置信息。
     *
     * @param joinPoint 切点对象,包含目标方法的详细信息。
     * @return 返回目标方法上标注的 Log 注解实例,如果不存在则返回 null。
     */
    private Log getApiOperationLogDescription(ProceedingJoinPoint joinPoint) {
        // 将 ProceedingJoinPoint 转换为 MethodSignature,以便获取方法相关的信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 通过 MethodSignature 获取目标方法对象
        Method method = signature.getMethod();
        // 从方法上尝试获取 Log 注解实例
        Log log = method.getAnnotation(Log.class);
        // 返回 Log 注解实例,如果方法上没有标注 Log 注解,则返回 null
        return log;
    }
}

创建测试类

/**
 * @date 2024/7/31 20:24
 * @description
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @Log(title = "测试管理", businessType = "测试")
    @RequestMapping("/test")
    public String test(String body) {
        return body;
    }
}

image.png

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值