自定义AOP注解实现日志记录

发布时间:2023年12月18日

一、引入依赖

<!--集成AOP实现接口的认证-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、编写日志切面的注解

/**
 * @author yandi
 * @description 同步记录的注解
 * @date 2023/8/8 14:38
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SyncRecord {

    /**
     * 同步方式(实时、手动全量)
     */
    String syncPattern() default SyncRecordString.REAL_TIME;
    /**
     * 同步数据
     */
    String syncData();

}

代码注解解释:

1.注解的定义:Java文件叫做Annotation,用@interface表示。

2.元注解:@interface上面按需要注解上一些东西,包括@Retention@Target@Document@Inherited四种。

3.注解的保留策略:

  @Retention(RetentionPolicy.SOURCE)   // 注解仅存在于源码中,在class字节码文件中不包含

  @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

  @Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

4.注解的作用目标:

  @Target(ElementType.TYPE)     // 接口、类、枚举、注解

  @Target(ElementType.FIELD)      // 字段、枚举的常量

  @Target(ElementType.METHOD)      // 方法

  @Target(ElementType.PARAMETER)     // 方法参数

  @Target(ElementType.CONSTRUCTOR)     // 构造函数

  @Target(ElementType.LOCAL_VARIABLE)     // 局部变量

  @Target(ElementType.ANNOTATION_TYPE)     // 注解

  @Target(ElementType.PACKAGE)      // 包

5.注解包含在javadoc中:

  @Documented

6.注解可以被继承:

  @Inherited

7.注解解析器:用来解析自定义注解。

三、编写切面类

/**
 * @author yandi
 * @description 记录同步记录的切面类
 * @date 2023/8/8 14:40
 */
@Slf4j
@Aspect
@Component
public class SyncRecordAspect {

    @Resource
    private OperationRecordsService operationRecordsService;


    /**
     * 第一个*代表返回类型不限
     * 第二个*代表所有类
     * 第三个*代表所有方法
     * (..) 代表参数不限
     */
    @Pointcut("execution(public * com.github.hollykunge.rest.*.*(..))")
    public void pointCut(){

    };

    @Around("pointCut() && @annotation(syncRecord)")
    public Object insertRecord(ProceedingJoinPoint proceedingJoinPoint,SyncRecord syncRecord) throws Throwable {
        log.info("执行方法前...");
        Object proceed = proceedingJoinPoint.proceed();
        log.info("执行方法后...");
        // 获取参数
        Map<String, Object> resultMap = getFieldsName(proceedingJoinPoint);
        // 转成json
        String argString = JSON.toJSONString(resultMap);
        // 插入同步记录
        String syncData = syncRecord.syncData();
        String syncPattern = syncRecord.syncPattern();
        OperationRecords operationRecords = new OperationRecords();
        operationRecords.setSyncData(argString);
        operationRecords.setSyncType(syncData);
        operationRecords.setSyncPattern(syncPattern);
        int count = operationRecordsService.insertOperationRecord(operationRecords);
        log.info("成功插入了{}类型数据{}条",syncData,count);
        return proceed;
    }


    @AfterThrowing(pointcut="pointCut() && @annotation(syncRecord)")
    public void afterThrowing(JoinPoint joinPoint,SyncRecord syncRecord){
        log.info("异常出现之后...afterThrowing");
        // 插入同步记录
        // 获取参数
        Map<String, Object> resultMap = getFieldsName(joinPoint);
        // 转成json
        String argString = JSON.toJSONString(resultMap);
        // 插入同步记录实体
        OperationRecords operationRecords = new OperationRecords();
        operationRecords.setSyncData(argString);
        int count = operationRecordsService.insertOperationRecord(operationRecords);
        log.info("插入了{}类型同步失败的数据{}条",syncData,count);

    }

    /**
     * 获取请求所携带的所有参数并且转成map的形式
     * @param joinPoint
     * @return
     */
    private static Map<String, Object> getFieldsName(JoinPoint joinPoint) {
        // 参数值
        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }

}

Spring5 中Aop五种通知执行的顺序

a. 在目标方法没有抛出异常的情况下:

环绕前置通知处理

@Before前置通知

invokeMethod()

@AfterReturning返回后通知:执行方法结束前执行

@After后置通知

环绕后置通知处理

b.在目标方法抛出异常的情况下:

环绕前置通知处理

@Before前置通知

invokeMethod()

@AfterThrowing异常通知:出现异常时执行

@After后置通知

c.如果存在多个切面:

多切面执行时,采用了责任链设计模式。

切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法。
文章来源:https://blog.csdn.net/qq_41978323/article/details/135067544
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。