You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Spring AOP/AspectJ拦截重写方法A()调用的Test类方法并统计耗时?

刚好之前做过类似的需求,给你分两种方案详细说下——Spring AOP和AspectJ,各自适用不同场景,你可以根据项目情况挑:

方案一:Spring AOP(适合大多数Spring项目)

Spring AOP基于动态代理实现,上手简单,但只对Spring管理的Bean生效,且无法拦截同一个类内部的方法调用(比如Test类自己的A()调用自己的b())。步骤如下:

  1. 引入依赖
    如果是Spring Boot项目,直接添加starter依赖即可:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 编写切面类
    定义一个带@Aspect和@Component注解的切面类,通过调用栈判断目标方法是否被Test类的重写A()调用,然后统计耗时:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
public class MethodInvocationTimeAspect {

    // 切入点匹配需要统计的b()、c()方法,可根据实际包/类调整
    @Around("execution(* com.yourproject..*(..)) && (execution(* *.b(..)) || execution(* *.c(..)))")
    public Object recordMethodTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 检查当前调用栈,判断是否来自Test类的A()方法
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        boolean isInvokedByTestA = Arrays.stream(stackTrace)
                .anyMatch(element -> "com.yourproject.Test".equals(element.getClassName())
                        && "A".equals(element.getMethodName()));

        if (!isInvokedByTestA) {
            // 不是目标调用链,直接放行
            return joinPoint.proceed();
        }

        // 统计耗时
        long start = System.nanoTime();
        Object result = joinPoint.proceed();
        long duration = (System.nanoTime() - start) / 1000000; // 转毫秒

        Method targetMethod = ((org.aspectj.lang.reflect.MethodSignature) joinPoint.getSignature()).getMethod();
        System.out.printf("[耗时统计] 方法%s被Test类的A()调用,耗时:%d ms%n", targetMethod.getName(), duration);

        return result;
    }
}
方案二:AspectJ(解决内部调用/非Bean方法场景)

如果你的场景存在同一个类内部调用(比如Test.A()调用Test.b()),或者目标方法不在Spring Bean中,那AspectJ是更好的选择——它通过字节码织入实现,能拦截所有方法调用,不受代理限制。

  1. 引入依赖
    除了Spring AOP的starter,还需要AspectJ的weaver:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>
  1. 开启AspectJ自动代理
    在application.properties中添加配置:
spring.aop.proxy-target-class=true
spring.aop.auto=true
  1. 编写AspectJ切面
    AspectJ支持更强大的切入点表达式,比如直接用cflow(控制流)匹配被Test.A()调用的方法:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MethodInvocationTimeAspect {

    // 切入点:匹配被Test类及其子类的A()方法调用的b()/c()方法
    @Around("(call(* *.b(..)) || call(* *.c(..))) && cflow(execution(* com.yourproject.Test+.A(..)))")
    public Object recordMethodTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.nanoTime();
        Object result = joinPoint.proceed();
        long duration = (System.nanoTime() - start) / 1000000;

        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[耗时统计] 方法%s被Test类的A()调用,耗时:%d ms%n", methodName, duration);

        return result;
    }
}

这里的Test+表示Test类及其所有子类,刚好适配你"被重写的A()"的需求。

额外注意事项
  • 如果需要更严谨的统计,建议用System.nanoTime()代替System.currentTimeMillis(),前者不受系统时间调整影响。
  • 不要直接用System.out打印,建议接入项目的日志框架(如SLF4J),方便后续排查。
  • 如果是编译时织入(CTW),还需要配置AspectJ的Maven/Gradle插件,不过Spring Boot项目一般用加载时织入(LTW),上面的配置就足够了。

内容的提问来源于stack exchange,提问作者Crazy-Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:58:49