SpringAOP的应用:统计接口执行时间

什么是AOP

Spring是java面向对象编程的主流框架,其主要思想有两个:反转控制(IOC)和面向切面(AOP)。

AOP(Aspect Oriented Programming)称为面向切面编程,是对面向对象(OOP)的补充和完善。

普通的面向对象基于封装、继承、多态等概念来建立一种对象层次结构,但仅限于纵向层次,无法做到横向关联。而日志、异常处理等功能涉及各个类,代码横向地散布在所有层次中,传统OOP难以复用这些代码。

而AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。

所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

日志中统计执行时间

对应代码中的关键方法,在日志中通过计时统计执行时间,可以监控功能执行是否正常,也可以观察程序的性能,作为优化效果的判断依据。

统计执行时间可以使用Stopwatch工具类,引入

import com.google.common.base.Stopwatch;

使用createStarted()开始计时,结束时用elapsed统计耗时。

代码示例如下:

import com.google.common.base.Stopwatch;

Stopwatch stopwatch = Stopwatch.createStarted();
/**
 * 功能代码
 */
log.info("xxxxx,elapsed:{} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));

不过这种方式只能统计一个功能的耗时,若现在有100个接口都需要统计耗时的话,显然是没办法每个都去这么写的。

这时就需要配合AOP技术来实现了。

使用AOP构建通用计时类

先放完整的实现:

@Component
@Aspect
@Log4j
public class TimingUtil {
 
    @Pointcut("execution(* com.xxx.xx.controller..*.*(..))")
    private void pointCut(){}
 
    @Around("pointCut()")
    public Object timing(ProceedingJoinPoint joinPoint){
        Object object;
        Stopwatch stopwatch = Stopwatch.createStarted();
        try{
            object = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
        log.info("API request completed, takes " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms.");
        return object;
    }
}

来看这里面的关键点:

1 Aspect和Component注解
Component注解对于Springboot来说相当于声明bean并添加至xml配置中,加了Component的类编译时就被视为Springboot项目的bean,进行拼装。

Aspect注解则声明了该类是一个切面。

2 PointCut注解
该注解用来声明AOP切面的切入点,或者说,告诉程序什么时候要使用切面介入。

execution表示在执行某些方法时介入,是PointCut最常用的介入方式。

表达式格式为:(返回类型,方法路径.方法名(参数类型))

在本例中,要介入的是接口类以获取时间,第一个* 代表不限制返回类型,接口类存放的路径在com.xxx.xx.controller中,后面..* 表示controller包下的所有类。

再往后.*表示这个类下的所有方法,(..)代表不限制传入的参数类型。

3 Around注解
在捕获切入点后,AOP类就可以进行切面上的操作了。

根据需要处理的行为方式不同,可以使用不同的注解标记,例如前置处理@Before、后置处理@AfterRunning、异常处理@AfterThrowing、环绕处理@Around等等。

这里我们要记录接口执行的时间,因此需要在接口调用前开始计时,在接口返回后停止计时,所以需要环绕处理。

需要在Around注解后标记切入方法名,因为可能不止一个切入。

使用Around注解时,切面方法必须使用入参ProceedingJoinPoint,代表切入点,切面方法返回类型默认选object(当然,如果可以确保所有返回类型一致,也可以直接定义)。

joinPoint.proceed()这个方法表示执行切入点的方法,在此之前的代码部分为前置处理,在此之后的代码部分为后置处理。

所以在执行proceed之前启动秒表,在执行后记录时间,这样对所有接口进行计时的功能就完成了。

你可能感兴趣的:(SpringAOP的应用:统计接口执行时间)