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

一、引入依赖

<!--集成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()执行时,去执行下一个切面或如果没有下一个切面执行目标方法。
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,我们可以使用自定义注解实现AOP(面向切面编程)。AOP是一种编程范型,它允许开发者在程序运行时动态地将代码切入到已有代码的特定位置。 下面是一个简单的示例,演示如何使用自定义注解实现AOP。 首先,我们需要定义一个自定义注解: ``` @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Loggable { } ``` 这个注解用来标记需要记录日志的方法。它的@Target注解指定了它只能用于方法上,@Retention注解指定了它的生命周期是运行时。 接下来,我们创建一个切面类,用来实现AOP的逻辑: ``` @Aspect @Component public class LoggingAspect { @Before("@annotation(com.example.Loggable)") public void logMethodCall(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("Method " + methodName + " called"); } } ``` 这个类使用Spring AOP框架提供的@Aspect注解来标记它是一个切面类。它的@Before注解指定了它要在被@Loggable注解标记的方法之前执行。JoinPoint参数包含了被拦截的方法的信息,我们可以从中获取方法名等信息。 最后,在需要记录日志的方法上加上@Loggable注解即可: ``` @Component public class MyService { @Loggable public void doSomething() { // do something } } ``` 当doSomething()方法被调用时,LoggingAspect中的logMethodCall()方法会被执行,记录方法调用信息。 这就是使用自定义注解实现AOP的基本步骤。当然,实际应用中会更加复杂,需要更多的切面逻辑和注解参数等。但是这个简单的示例可以帮助你理解如何使用自定义注解实现AOP

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值