Spring Boot 监控:AOP vs Filter vs Java Agent

01前言

在 高并发 + 微服务 中,

传统 手动埋点(System.currentTimeMillis())就像用体温计量火箭速度------代码侵入、重复劳动、维护爆炸。

下文是无侵入、高精度、全链路 监控 API 耗时,全程不碰业务代码的方案!

02实战:6 种方案横向对比

03方案详解

方案 1:StopWatch(临时调试)

代码:

java 复制代码
@GetMapping("/query")
public ResponseEntity<String> query() throws Exception {
    StopWatch stopWatch = new StopWatch(); // 创建计时器
    stopWatch.start(); // 开始计时
    TimeUnit.MILLISECONDS.sleep(new Random().nextLong(2000)); // 模拟业务
    stopWatch.stop(); // 结束计时
    System.out.printf("**方法耗时:%dms**%n", stopWatch.getTotalTimeMillis());
    return ResponseEntity.ok("api query...");
}

注解:

  1. StopWatch 是 Spring 提供的轻量级计时工具。
  2. 侵入性强:需手动插入到业务代码中。
  3. 适合 临时调试,生产环境 不推荐。

方案 2:AOP 切面(无侵入)

代码:

java 复制代码
@Aspect // 声明切面
@Component
public class PerformanceAspect {

    private static final Logger logger = LoggerFactory.getLogger("api.timed");

    @Around("@annotation(org.springframework.web.bind.annotation.GetMapping)") // 拦截所有 Get 请求
    public Object recordExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch();
        sw.start();
        Object result = pjp.proceed(); // 执行原方法
        sw.stop();
        logger.info("**方法【{}】耗时: {}ms**", pjp.getSignature(), sw.getTotalTimeMillis());
        return result;
    }
}

注解:

  1. @Around 注解拦截所有 GetMapping 方法,零侵入。
  2. 通过 ProceedingJoinPoint 获取方法签名,精准定位慢接口。
  3. 缺点:无法监控非 Spring 管理的类(如手动 new 的对象)。

方案 3:拦截器(Controller 层统一监控)

代码:

java 复制代码
public class TimedInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        request.setAttribute("startTime", System.currentTimeMillis()); // 记录开始时间
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        long startTime = (long) request.getAttribute("startTime");
        long cost = System.currentTimeMillis() - startTime;
        System.out.printf("**请求【%s】耗时: %dms**%n", request.getRequestURI(), cost);
    }
}

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TimedInterceptor())
                .addPathPatterns("/api/**"); // 拦截所有 /api 下的请求
    }
}

注解:

  1. preHandle 在请求前记录时间,afterCompletion 在请求后计算耗时。
  2. 只适用于 Controller 层,无法监控 Service/DAO 层方法。
  3. 配置简单,适合 Web 项目快速接入。

方案 4:Filter(Servlet 级通用监控)

代码:

java 复制代码
@Component
public class RequestTimingFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(RequestTimingFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        long startTime = System.nanoTime(); // 高精度计时
        try {
            chain.doFilter(request, response); // 继续处理请求
        } finally {
            long duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
            logger.info("**[{}] {} - {}ms (Status: {})**",
                    httpRequest.getMethod(),
                    httpRequest.getRequestURI(),
                    duration,
                    ((HttpServletResponse) response).getStatus());
        }
    }
}

注解:

  1. 全局生效,所有请求(包括静态资源)都会被监控。
  2. 粒度较粗,无法定位具体方法耗时。
  3. 可通过 excludePaths 过滤不需要监控的 URI。

方案 5:事件监听(零侵入全局统计)

代码:

java 复制代码
@Component
public class TimedListener {
    @EventListener(ServletRequestHandledEvent.class)
    public void recordTimed(ServletRequestHandledEvent event) {
        System.err.println("**请求监控事件: " + event + "**");
    }
}

注解:

  1. Spring 内部事件机制,无需任何配置。
  2. 只能获取 总耗时,无法拆分各阶段耗时。
  3. 适合 快速接入,不适合精细化监控。

方案 6:Micrometer + Prometheus(可视化监控)

代码:

java 复制代码
@RestController
public class ApiController {

    @Timed(value = "api.query", description = "查询业务接口") // 关键注解
    @GetMapping("/query")
    public ResponseEntity<String> query() throws Exception {
        TimeUnit.MILLISECONDS.sleep(new Random().nextLong(2000));
        return ResponseEntity.ok("api query...");
    }
}

配置:

java 复制代码
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: prometheus # 暴露 Prometheus 端点
  metrics:
    export:
      prometheus:
        enabled: true
Prometheus 配置:

- job_name: "spring-boot-app"
  metrics_path: "/actuator/prometheus"
  static_configs:
    - targets: ["localhost:8080"]

注解:

  1. @Timed 注解 零侵入,自动记录耗时指标。
  2. Prometheus + Grafana 可视化展示,支持 报警规则。
  3. 缺点:需引入额外依赖,适合生产环境。

方案 7:SkyWalking(分布式全链路追踪)

启动命令:

java 复制代码
java -javaagent:/path/skywalking-agent.jar \
     -Dskywalking.agent.service_name=your-app \
     -jar your-app.jar

注解:

  1. 无代码侵入,通过 Java Agent 自动注入。
  2. 支持 分布式链路追踪,跨服务追踪耗时。
  3. 适合微服务架构,强烈推荐!

如何选?

• 单体应用:Micrometer + Prometheus(可视化 + 报警)。

• 微服务架构:SkyWalking(全链路追踪)。

• 临时调试:Arthas(在线诊断神器)。

相关推荐
摇滚侠1 小时前
Spring Boot 3零基础教程,WEB 开发 静态资源默认配置 笔记27
spring boot·笔记·后端
wb043072013 小时前
性能优化实战:基于方法执行监控与AI调用链分析
java·人工智能·spring boot·语言模型·性能优化
Chen-Edward6 小时前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
magic334165636 小时前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
小学鸡!6 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
番茄Salad7 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
摇滚侠10 小时前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠10 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
橘子海全栈攻城狮11 小时前
【源码+文档+调试讲解】基于SpringBoot + Vue的知识产权管理系统 041
java·vue.js·人工智能·spring boot·后端·安全·spring
Json_12 小时前
学习springBoot框架-开发一个酒店管理系统,熟悉springboot框架语法~
java·spring boot·后端