Spring-Boot-Aop实现接口耗时计算

Spring中一个重要的点就是面向切面编程,即AOP,可以实现程序中功能的解耦,让一些类共享相同的行为动作。Spring中AOP的实现主要通过JDK的动态代理和CGLIB实现。

AOP:即 在不修改源代码的情况下对代码进行增强。

一、AOP通知(增强)类型

  • before(前置通知): 在方法开始执行前执行

  • after(后置通知): 在方法执行后执行

  • afterReturning(返回后通知): 在方法返回后执行

  • afterThrowing(异常通知): 在抛出异常时执行

  • around(环绕通知): 在方法执行前和执行后都会执行

执行顺序:

around > before > around > after > afterReturning

二、使用自定义注解 和 Aop 实现接口耗时计算(环绕通知)

  1. 引入依赖

  2.         <!--Aop-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
  3. 创建自定义注解

  4. package com.itcast.commons.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @ClassName: MonitorExecTime
     * @author: anqin
     * @Description: 监控执行时间
     * @date: 2023/1/27 21:04
     */
    @Target({ElementType.METHOD})
    @Retention(value = RetentionPolicy.RUNTIME)
    @Documented
    public @interface MonitorExecTime {
        String description() default "";
    }
    
    
  5. 创建切面类

  6. package com.itcast.system;
    
    import com.itcast.commons.annotation.MonitorExecTime;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import java.lang.reflect.Method;
    
    /**
     * @ClassName: RunningTimeAspect
     * @author: anqin
     * @Description: 接口运行时间计算
     * @date: 2023/1/27 20:21
     */
    @Slf4j
    @Aspect
    @Component
    @Order(1)
    public class RunningTimeAspect {
        /**
         * 接口耗时超过设置打印耗时信息 - 100 ms
         */
        private static final Long API_EXEC_TIME_PRINT_START = 100L;
    
        /**
         * 接口耗时超过设置发送监控通知 - 3000 ms
         **/
        private static final Long API_EXEC_TIME_MONITOR_START = 3000L;
    
        @Around(value = "@annotation(com.itcast.commons.annotation.MonitorExecTime)")
        public Object interfaceCalculation(ProceedingJoinPoint pj) {
    
            // 开始时间
            long startTime = System.currentTimeMillis();
    
            // 获取 连接点签名
            Signature signature = pj.getSignature();
            if (!(signature instanceof MethodSignature)) {
                return this.proceed(pj);
            }
    
            MethodSignature methodSignature = (MethodSignature) signature;
            // 获取 通知的方法
            Method method = methodSignature.getMethod();
    
    
            // 切入方法 的 全类名
            String name = methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();
    
            // 获取注解描述
            MonitorExecTime annotation = method.getAnnotation(MonitorExecTime.class);
            String description = annotation.description();
    
    
            try {
                return this.proceed(pj);
            } finally {
                this.computationalConsumptionTime(startTime, name, description);
            }
        }
    
        /**
         * 消耗时间计算
         */
        private void computationalConsumptionTime(Long startTime, String canonicalName, String description) {
            long consumptionTime = System.currentTimeMillis() - startTime;
    
            String template = "%s -- %s -- 耗时%d ms";
            String printInfo = String.format(template, description,canonicalName,consumptionTime);
    
            log.info(printInfo);
    
        }
    
        private Object proceed(ProceedingJoinPoint pj) {
            try {
                return pj.proceed(pj.getArgs());
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
  7. 结果