创建一个项目工程
引入相关依赖
<!-- 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;
}
}