最近在刷牛客:使用Spring AOP实现性能监控时

题目:4.使用Spring AOP实现性能监控时,下列哪种方式无法获取方法执行时间?

A在@Around增强中通过ProceedingJoinPoint.proceed()调用前后计算时间差

B在@AfterReturning增强中通过JoinPoint获取方法开始时间戳

C使用@Before记录开始时间并存入ThreadLocal,在@After中计算耗时

D通过实现MethodInterceptor接口在invoke方法中计算执行时间

项目目标

  • 使用 Spring Boot

  • 演示 4 种 AOP 方式

  • 验证 哪一种不能获取方法执行时间

  • 结构清晰,适合练习和面试


一、项目结构(强烈建议你照着建)

复制代码
spring-aop-demo
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── example
                    └── aopdemo
                        ├── AopDemoApplication.java
                        ├── service
                        │   └── UserService.java
                        ├── aspect
                        │   ├── TimeAspect.java
                        │   └── TimeInterceptorConfig.java
                        └── interceptor
                            └── TimeMethodInterceptor.java

二、pom.xml(完整)

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-aop-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

三、启动类

java 复制代码
package com.example.aopdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AopDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AopDemoApplication.class, args);
    }
}

四、业务类(被代理对象)

java 复制代码
package com.example.aopdemo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void login() throws InterruptedException {
        Thread.sleep(300); // 模拟方法执行
        System.out.println("执行 login 方法");
    }
}

五、AOP 切面(核心)

java 复制代码
package com.example.aopdemo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimeAspect {

    /* ========== A. @Around(能统计时间) ========== */
    @Around("execution(* com.example.aopdemo.service..*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long end = System.currentTimeMillis();
        System.out.println("[Around] 耗时:" + (end - start));
        return result;
    }

    /* ========== B. @AfterReturning(不能统计时间) ========== */
    @AfterReturning("execution(* com.example.aopdemo.service..*(..))")
    public void afterReturning(JoinPoint jp) {
        System.out.println("[AfterReturning] 无法获取开始时间");
    }

    /* ========== C. @Before + @After + ThreadLocal(能统计时间) ========== */
    private static final ThreadLocal<Long> TIME =
            ThreadLocal.withInitial(System::currentTimeMillis);

    @Before("execution(* com.example.aopdemo.service..*(..))")
    public void before() {
        TIME.set(System.currentTimeMillis());
    }

    @After("execution(* com.example.aopdemo.service..*(..))")
    public void after() {
        long cost = System.currentTimeMillis() - TIME.get();
        System.out.println("[Before+After] 耗时:" + cost);
        TIME.remove();
    }
}

六、MethodInterceptor 方式(能统计时间)

1. 拦截器

java 复制代码
package com.example.aopdemo.interceptor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class TimeMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = invocation.proceed();
        long end = System.currentTimeMillis();
        System.out.println("[MethodInterceptor] 耗时:" + (end - start));
        return result;
    }
}

2. 配置类

java 复制代码
package com.example.aopdemo.aspect;

import com.example.aopdemo.interceptor.TimeMethodInterceptor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TimeInterceptorConfig {

    @Bean
    public ProxyFactoryBean userServiceProxy(UserService userService) {
        ProxyFactoryBean factory = new ProxyFactoryBean();
        factory.setTarget(userService);
        factory.addAdvice(new TimeMethodInterceptor());
        return factory;
    }
}

七、测试方式(最简单)

启动项目后访问:

复制代码
GET http://localhost:8080/test

或者直接写测试类:

java 复制代码
@SpringBootTest
class AopDemoApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    void testAop() throws InterruptedException {
        userService.login();
    }
}

八、你运行后会看到类似输出

复制代码
[Around] 耗时:302
[Before+After] 耗时:302
[MethodInterceptor] 耗时:301
[AfterReturning] 无法获取开始时间
执行 login 方法

✅ 一眼就能验证 B 是错误的

相关推荐
我命由我123453 小时前
Android Studio - Android Studio 自定义预览尺寸
android·java·ide·java-ee·android studio·android-studio·android runtime
mCell4 小时前
你以为短链接只是 Hash + 301/302?
后端·算法·架构
咖啡八杯4 小时前
GoF设计模式——迭代器模式
java·后端·设计模式·迭代器模式
AIGS0015 小时前
企业AI落地的关键认知:向量空间JBoltAI的本体语义平台
java·人工智能·人工智能ai大模型应用
KaMeidebaby5 小时前
卡梅德生物技术快报|抗体亲和力成熟工业化调控新机制:差异性浆细胞增殖工艺优化思路
java·开发语言·人工智能·算法·机器学习·架构·spark
醉城夜风~7 小时前
Java详解经典算法题:接雨水(三种实现方案+原理剖析)
java·开发语言·算法
数智化转型推荐官8 小时前
2026统一身份管理系统五大发展趋势解读
java·运维·微服务
看菜鸡互8 小时前
探索用 SlideML 让大模型生成 PPT 的实验方法
java·运维
IT_陈寒8 小时前
SpringBoot自动配置不是你以为的那样的智能
前端·人工智能·后端
程序员张310 小时前
SpringBoot集成BCrypt密码加密库
java·spring boot·后端