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(在线诊断神器)。

相关推荐
右手嘚温暖14 分钟前
分布式事务Seata、LCN的原理深度剖析
spring boot·分布式·后端·spring·spring cloud·中间件·架构
麦兜*2 小时前
Spring Boot集成方案 + Elasticsearch向量检索,语义搜索核弹
java·spring boot·python·spring·elasticsearch·spring cloud·系统架构
柊二三5 小时前
spring boot开发中的资源处理等问题
java·spring boot·后端
一枚小小程序员哈5 小时前
基于springboot的宠物商城设计与实现
java·spring boot·spring·eclipse·tomcat·maven·宠物
风与尘6 小时前
RabbitMQ延时队列的两种实现方式
spring boot·分布式·中间件·rabbitmq
_码农121387 小时前
简单spring boot项目,之前练习的,现在好像没有达到效果
java·spring boot·后端
巴拉巴巴巴拉8 小时前
Spring Boot 整合 Web 开发全攻略
spring boot
写代码的比利10 小时前
Spring 调试终于不再痛苦了
spring boot·spring·intellij idea
一只爱撸猫的程序猿11 小时前
构建一个简单的亿级数据迁移方案案例
spring boot·数据分析·ai编程
风象南12 小时前
告别YAML,在SpringBoot中用数据库配置替代配置文件
spring boot·后端