0
点赞
收藏
分享

微信扫一扫

【项目实战功能】自定义注解实现代码的执行耗时记录

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,阿里云专家博主,华为云云享专家✌

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

1、什么是自定义注解

自定义注解是一种在Java编程语言中使用的特殊标记。它允许开发人员在代码中添加自己的元数据,以便在运行时使用。自定义注解可以应用于类、方法、字段等程序元素上,并可以包含自定义的属性。通过使用自定义注解,开发人员可以在代码中添加额外的信息,以便在编译时或运行时进行处理。这使得开发人员能够根据自己的需求创建更具表现力和灵活性的代码。

2、代码

先设计自定义的注解

package com.pany.camp.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *
 * @description:  日志注解
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-07-05 17:04
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AiocLogTime {

    String message() default "";
}

设计切面类:

package com.pany.camp.aspect;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 *
 * @description:  切面
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-07-05 17:38
 */
@Slf4j
@Aspect
@Component
public class AiocLogAspect {

    @Pointcut("@annotation(com.pany.camp.aspect.AiocLogTime)")
    public void aiocLogAspect() {
    }

    @Around("aiocLogAspect() && @annotation(com.pany.camp.aspect.AiocLogTime)")
    public Object aiocLogTime(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = StopWatch.create();
        stopWatch.start();

        // 执行业务
        Object result = joinPoint.proceed();
        stopWatch.stop();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String clazz = signature.getDeclaringTypeName();
        String method = signature.getMethod().getName();
        long time = stopWatch.getTime();
        log.info("{}-{}, business execution time : {}", clazz, method, time);
        return result;
    }
}

测试的客户端类

package com.pany.camp.aspect;

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 *
 * @description:  测试类
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-07-05 17:38
 */
@Slf4j
@RestController
@RequestMapping("/aspect/v1")
public class ClientController {

    @AiocLogTime(message = "测试")
    @RequestMapping("/test")
    public void test() {
        ThreadUtil.sleep(1000);
        log.info("do .....");
    }
}

请求 http://localhost:19096/console-service/aspect/v1/test 解释: console-service 是项目中的 context-path

在这里插入图片描述

3、@Retention 和 RetentionPolicy

@Retention 是一个用于指定注解保留策略的元注解(即用于注解其他注解的注解)。它可以应用于自定义注解上,用于指定注解在编译后的字节码中的保留策略。

@Retention 注解有三个可选的保留策略:

  1. RetentionPolicy.SOURCE:注解仅保留在源代码中,编译后的字节码中不包含该注解。这意味着注解在运行时不可见,只用于编译阶段的静态检查。

  2. RetentionPolicy.CLASS:注解保留在编译后的字节码中,但在运行时不可见。这是默认的保留策略,如果在自定义注解上未显式指定保留策略,则会使用该策略。

  3. RetentionPolicy.RUNTIME:注解保留在编译后的字节码中,并在运行时可通过反射机制获取到。这意味着可以在运行时通过注解处理器等方式处理该注解。

通常,如果你需要在运行时通过反射获取注解信息,就需要使用 RetentionPolicy.RUNTIME 保留策

4、@Target 和 ElementType

@Target 是一个用于指定注解应用目标的元注解。它可以应用于自定义注解上,用于指定注解可以应用于哪些程序元素上。

@Target 注解接受一个 ElementType[] 类型的参数,用于指定目标元素类型。常见的目标元素类型包括:

  1. ElementType.TYPE:可以应用于类、接口、枚举等类型的声明。
  2. ElementType.FIELD:可以应用于字段(成员变量)的声明。
  3. ElementType.METHOD:可以应用于方法的声明。
  4. ElementType.PARAMETER:可以应用于方法参数的声明。
  5. ElementType.CONSTRUCTOR:可以应用于构造函数的声明。
  6. ElementType.LOCAL_VARIABLE:可以应用于局部变量的声明。
  7. ElementType.ANNOTATION_TYPE:可以应用于注解的声明。
  8. ElementType.PACKAGE:可以应用于包的声明。

通过使用 @Target 注解,可以限制自定义注解的使用范围,确保注解只能应用于指定的程序元素上。例如,如果你只希望自定义注解应用于类和方法上,可以在注解定义时使用 @Target(ElementType.TYPE, ElementType.METHOD) 来指定。这样,如果有人试图在字段或参数上使用该注解,编译器将会报错。

5、@Pointcut

@Pointcut 是一个用于定义切点(Pointcut)的注解。切点是在面向切面编程(AOP)中使用的一个概念,它表示在程序执行过程中,哪些方法或代码段应该被拦截和增强。

@Pointcut 注解通常与 @Aspect 注解一起使用,用于定义切点表达式。切点表达式是一种用于匹配目标方法的模式,它可以基于方法的名称、参数、注解等进行匹配。

例如,下面是一个使用 @Pointcut 注解定义切点的示例:

@Aspect
public class LoggingAspect {
     @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}
     // 其他增强方法
}

在上面的示例中,@Pointcut 注解定义了一个切点表达式,它匹配 com.example.service 包下的所有方法(任意返回类型、任意方法名、任意参数)。serviceMethods() 方法本身没有任何实际作用,它只是用于定义切点,供其他增强方法使用。

通过使用 @Pointcut 注解,可以更加灵活地定义切点,以便在 AOP 中选择性地拦截和增强程序的执行。

6、@Around

@Around 是一个用于定义环绕通知(Around Advice)的注解。环绕通知是面向切面编程(AOP)中的一种通知类型,它可以在目标方法执行前后以及异常抛出时进行拦截和增强。

@Around 注解通常与 @Aspect 注解一起使用,用于标识一个环绕通知方法。环绕通知方法可以控制目标方法的执行,可以在目标方法执行前后添加额外的逻辑,还可以决定是否继续执行目标方法或者直接返回结果。

例如,下面是一个使用 @Around 注解定义环绕通知的示例:

@Aspect
public class LoggingAspect {
   
    @Around("execution(* com.example.service.*.*(..))")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        // 在目标方法执行前
        System.out.println("Before method execution...");
         // 执行目标方法
        Object result = joinPoint.proceed();
         // 在目标执行后
        System.out.println("After method execution...");
         // 返回结果
        return result;
    }
    // 其他增强方法
}

在上面的示例中,@Around 注解标识了一个环绕通知方法 logMethodExecution()。该方法接收一个 ProceedingJoinPoint 对象作为参数,通过调用其 proceed() 方法来执行目标方法。在目标方法执行前后,可以添加额外的逻辑,例如打印日志或处理异常等。 通过使用 @Around 注解,可以更加灵活地控制目标方法的执行过程,并在必要时进行拦截和增强。

7、ProceedingJoinPoint

ProceedingJoinPoint 是一个用于表示连接点(Join Point)的接口,它是在面向切面编程(AOP)中使用的一个概念。连接点表示程序执行过程中的一个特定位置,例如方法调用、方法执行、异常抛出等。

ProceedingJoinPoint 接口扩展了 JoinPoint 接口,并添加了一个 proceed() 方法。该方法用于执行目标方法,并可以在环绕通知中控制目标方法的执行。在环绕通知中,可以通过调用 proceed() 方法来继续执行目标方法,也可以选择不调用 proceed() 方法来终止目标方法的执行并返回自定义的结果。

例如,在上面的示例中,logMethodExecution() 方法的参数类型为 ProceedingJoinPoint,可以通过调用其 proceed() 方法来执行目标方法。在调用 proceed() 方法之前和之后,可以添加额外的逻辑,以实现拦截和增强的效果。

通过使用 ProceedingJoinPoint,可以在 AOP 中获取和控制目标方法的执行过程,并在必要时对其进行干预和处理。 在这里插入图片描述

💕💕 本文由激流原创,原创不易,感谢支持 💕💕喜欢的话记得点赞收藏啊 在这里插入图片描述

举报

相关推荐

0 条评论