最近在刷牛客:使用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 是错误的

相关推荐
Java水解4 小时前
深入浅出多包架构(Monorepo)
后端
华清远见成都中心4 小时前
C 语言内存管理深度解析:malloc/free 与嵌入式堆栈分配策略
java·c语言·算法
YANZ2224 小时前
亚马逊绿标(CPF):从环保认证到跨境流量新引擎
java·大数据·人工智能·搜索引擎·百度
超梦dasgg4 小时前
智慧充电系统订单服务Java 实现方案
java·开发语言·微服务
JWASX4 小时前
【RocketMQ 生产者和消费者】- 事务源码分析(2)
java·rocketmq·java-rocketmq
手握风云-4 小时前
Spring AI:让大模型住进 Spring 生态(四)
java·后端·spring
南滑散修4 小时前
红黑树-非黑即红
java·开发语言
Java面试题总结4 小时前
Spring Boot:别再重复造轮子,这些内置功能香麻了
java·spring boot·后端
迷糊小白告5 小时前
Java微服务——SpringCloud
java·spring cloud·微服务