题目: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 是错误的