最近在刷牛客:使用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 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>三、启动类
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); } }四、业务类(被代理对象)
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 切面(核心)
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. 拦截器
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. 配置类
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或者直接写测试类:
@SpringBootTest class AopDemoApplicationTests { @Autowired private UserService userService; @Test void testAop() throws InterruptedException { userService.login(); } }八、你运行后会看到类似输出
[Around] 耗时:302 [Before+After] 耗时:302 [MethodInterceptor] 耗时:301 [AfterReturning] 无法获取开始时间 执行 login 方法✅ 一眼就能验证B 是错误的
