在 Spring Boot 项目中,可以通过 AOP(面向切面编程) 定义一个监控切面,用于统一收集方法的执行时间、调用次数、异常等信息。以下是完整的实现步骤和代码示例:
一、添加依赖
在 pom.xml 中添加 Spring AOP 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、定义监控切面类
创建一个切面类 MonitorAspect,包含以下核心组件:
1. 定义切点(Pointcut)
使用 @Pointcut 注解声明需要监控的方法范围(如某个包下的所有方法)。
2. 定义通知(Advice)
使用 @Around 等通知类型,在目标方法执行前后插入监控逻辑。
package com.example.demo.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Aspect // 声明为切面类
@Component // 纳入 Spring 容器管理
@Slf4j // 使用 Lombok 日志注解
public class MonitorAspect {
/**
* 定义切点:监控 com.example.demo.service 包下的所有方法
*/
@Pointcut("execution(* com.example.demo.service.*.*(..))")
public void servicePointcut() {}
/**
* 环绕通知:记录方法执行时间
*/
@Around("servicePointcut()")
public Object monitorMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().toShortString();
try {
// 执行目标方法
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
// 记录方法执行时间
log.info("[Monitor] 方法 {} 执行耗时: {} ms", methodName, executionTime);
return result;
} catch (Throwable e) {
// 记录异常信息
log.error("[Monitor] 方法 {} 发生异常: {}", methodName, e.getMessage());
throw e;
}
}
}
三、测试监控切面
1. 创建测试 Service
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class DemoService {
public String process() {
// 模拟业务逻辑
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}
public void errorMethod() {
throw new RuntimeException("模拟异常");
}
}
2. 调用测试方法
package com.example.demo;
import com.example.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private DemoService demoService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
demoService.process(); // 触发正常方法
demoService.errorMethod(); // 触发异常方法
}
}
3. 查看日志输出
控制台会输出监控信息:
[Monitor] 方法 DemoService.process() 执行耗时: 105 ms
[Monitor] 方法 DemoService.errorMethod() 发生异常: 模拟异常
四、扩展功能
1. 自定义监控注解
可以定义一个自定义注解 @Monitor,用于标记需要监控的方法。
package com.example.demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitor {
String value() default "";
}
修改切点定义:
@Pointcut("@annotation(com.example.demo.annotation.Monitor)")
public void monitorAnnotation() {}
使用注解标记需要监控的方法:
@Service
public class DemoService {
@Monitor
public String process() { /* ... */ }
}
2. 统计方法调用次数
在切面类中添加计数器:
private ConcurrentHashMap<String, AtomicInteger> methodCallCount = new ConcurrentHashMap<>();
@Around("servicePointcut()")
public Object monitorMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
methodCallCount.putIfAbsent(methodName, new AtomicInteger(0));
methodCallCount.get(methodName).incrementAndGet();
// ... 原有逻辑
}
五、注意事项
-
AOP 生效条件:
- 目标方法需通过 Spring 代理调用(同类内部方法调用不会触发切面)。
- 确保切面类所在的包被 Spring 扫描到。
-
性能影响:
- 避免在切面中执行耗时操作(如远程调用),否则可能影响系统性能。
-
切入点表达式优化:
- 使用更精确的表达式(如
@annotation)减少不必要的拦截。
- 使用更精确的表达式(如
通过以上步骤,即可在 Spring Boot 项目中实现一个完整的监控切面。