SpringBoot中使用AOP实现耗时统计

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

一、AOP中相关术语

1、切面(Aspect):

是指横切多个对象的关注点的一个模块化,切面通过常规类(基本模式方法)或者通过使用了注解@Aspect的常规类来实现。

2、连接点(Joint point):

就是spring中允许使用通知的地方,基本上每个方法前后抛异常时都可以是连接点

3、通知(Advice):

需要完成的工作叫做通知,也叫增强,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用。

4、切点(Pointcut):

是指匹配连接点的一个断言。通知是和一个切入点表达式关联的,并且在任何被切入点匹配的连接点上运行(举例,使用特定的名字执行某个方法)。AOP的核心就是切入点表达式匹配连接点的思想。Spring默认使用AspectJ切入点表达式语言。

5、引入(Introduction):

代表了对一个类型额外的方法或者属性的声明。

6、目标对象(Target object):

是指被一个或多个切面通知的那个对象。

7、AOP代理(AOP proxy):

是指通过AOP框架创建的对象,用来实现切面合约的(执行通知方法等等)。

8、织入(Weaving):

将切面和其他应用类型或者对象连接起来,创建一个被通知对象。在目标对象的生命周期中,有多个点可以进行织入:

  • 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
  • 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
  • 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。

二、AOP通知(增强)类型

  • before(前置通知): 在方法开始执行前执行
  • after(后置通知): 在方法执行后执行
  • afterReturning(返回后通知): 在方法返回后执行
  • afterThrowing(异常通知): 在抛出异常时执行
  • around(环绕通知): 在方法执行前和执行后都会执行

    执行顺序:

    around > before > around > after > afterReturning

三、AOP实现方法耗时统计并打印日志

假设有个test类,里面有testMethod方法,现在需要统计方法运行的耗时时间并记录在日志中:

public class Test{
    public void testMethod(){
        ......
    }

}

创建一个AOP类进行统计:



import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
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.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Date;


@Aspect
@Component
@Slf4j
public class JobRunTimeAop {
    @Pointcut(" execution(* com.test.testMethod(..))" )
    public void methodPointCut() {
    }

    @Around("methodPointCut()")
    public Object runTimeStatistics(ProceedingJoinPoint pjp) throws Throwable {
        Signature signature = pjp.getSignature();
        //被代理的类的类名
        String className = pjp.getTarget().getClass().getName();
        //方法名
        String methodName = signature.getName();
        //参数数组
        Object[] requestParams = pjp.getArgs();
        StringBuffer sb = new StringBuffer();
        for(Object requestParam : requestParams){
            if(requestParam!=null){
                sb.append(JSON.toJSONString(requestParam));
                sb.append(",");
            }
        }
        String requestParamsString = sb.toString();
        if(requestParamsString.length()>0){
            requestParamsString = requestParamsString.substring(0, requestParamsString.length() - 1);
        }
        //接口应答前打印日志
        log.info(String.format("【%s】类的【%s】方法,请求参数:%s", className, methodName, requestParamsString));
        //接口调用开始响应起始时间
        Date startDate = DateUtil.date();
        //正常执行方法,即让方法进行执行
        Object response = pjp.proceed();
        Date endDate = DateUtil.date();
        // 获取开始时间和结束时间的时间差
        long betweenDate = DateUtil.between(startDate, endDate, DateUnit.MS);
        // 格式化时间
        String formatBetween = DateUtil.formatBetween(betweenDate, BetweenFormatter.Level.MILLISECOND);

        //接口应答之后打印日志
        log.info(String.format("【%s】类的【%s】方法,应答参数:%s", className, methodName, JSON.toJSONString(response)));
        //接口耗时
        log.info(String.format("接口【%s】总耗时(毫秒):%s", methodName, formatBetween));

        return response;
    }
}

 

 

 

你可能感兴趣的:(Java学习,框架及三方组件,java,spring,boot,aop)