首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring AOP 實現方法執行耗時監控:@Around 切面、閾值告警、生產級最佳實踐

     編輯:Java 相關知識

為什麼用 AOP 做耗時監控?

  • **非侵入**:業務代碼零修改,橫切關注點集中管理。
  • **統一標準**:所有 Service/Repository/Controller 方法統一埋點、統一日誌格式。
  • **動態控制**:運行期調整閾值、開關、采樣率,無需重新部署。

核心切面實現

@Aspect
@Component
@Slf4j // Lombok 日誌
public class MethodLatencyAspect {

    /** 耗時閾值(毫秒),超過才記錄 WARN,避免日誌淹沒 */
    private static final long SLOW_THRESHOLD_MS = 100;

    /** 切點:service 包下所有類的所有方法 */
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void serviceLayer() {}

    @Around("serviceLayer()")
    public Object measure(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        String methodSignature = getSignature(pjp);
        try {
            return pjp.proceed();
        } finally {
            long elapsedMs = (System.nanoTime() - start) / 1_000_000;
            if (elapsedMs > SLOW_THRESHOLD_MS) {
                log.warn("[SLOW] {} 耗時 {} ms", methodSignature, elapsedMs);
            } else if (log.isDebugEnabled()) {
                log.debug("[TIMING] {} 耗時 {} ms", methodSignature, elapsedMs);
            }
        }
    }

    private String getSignature(ProceedingJoinPoint pjp) {
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        return ms.getDeclaringTypeName() + "." + ms.getName() + Arrays.toString(ms.getParameterTypes());
    }
}

進階:參數/返回值/異常 記錄(慎用,防敏感數據洩露)

@Around("serviceLayer()")
public Object measureWithDetails(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.nanoTime();
    String sig = getSignature(pjp);
    Object[] args = pjp.getArgs();
    String argsJson = safeToJson(args); // 過濾密碼、Token 等敏感字段
    log.debug("▶ {} 輸入: {}", sig, argsJson);
    try {
        Object result = pjp.proceed();
        long ms = (System.nanoTime() - start) / 1_000_000;
        String retJson = safeToJson(result);
        if (ms > SLOW_THRESHOLD_MS) {
            log.warn("⏱ {} 耗時 {} ms | 出參: {}", sig, ms, retJson);
        } else {
            log.debug("⏱ {} 耗時 {} ms | 出參: {}", sig, ms, retJson);
        }
        return result;
    } catch (Throwable ex) {
        long ms = (System.nanoTime() - start) / 1_000_000;
        log.error("✖ {} 耗時 {} ms 異常: {}", sig, ms, ex.getMessage(), ex);
        throw ex;
    }
}

private String safeToJson(Object obj) {
    try {
        // 使用 Jackson 忽略 @JsonIgnore、@JsonProperty(access=WRITE_ONLY) 字段
        return new ObjectMapper().writeValueAsString(obj);
    } catch (Exception e) {
        return obj == null ? "null" : obj.getClass().getSimpleName() + "@" + obj.hashCode();
    }
}

配置啟用 AOP(Spring Boot 自動,傳統 XML 需手動)

<!-- applicationContext.xml -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 或 CGLIB 強制代理類 -->

Spring Boot 只需引入 spring-boot-starter-aop,自動啟用 @EnableAspectJAutoProxy。

生產級優化清單

優化點做法
性能開銷使用 System.nanoTime() 而非 currentTimeMillis();僅 WARN 級別輸出完整參數,DEBUG 級別簡略。
采樣率高吞吐接口配置 @Around("serviceLayer() && @annotation(Sampled)"),自定義註解控制采樣。
異步日誌Log4j2 AsyncAppender / Logback AsyncAppender,避免日誌 I/O 阻塞業務線程。
指標收集接入 Micrometer + Prometheus,將耗時作為 Histogram 指標導出,Grafana 告警,而非依賴文本日誌。
分布式追蹤結合 SkyWalking / Zipkin / OpenTelemetry,自動獲取 Span 耗時,無需手寫切面。

常見誤區

  1. 切面不生效:Bean 未被容器管理(缺少 @Component)、方法為 private/final/static、類內部調用繞過代理(需注入自身或使用 AopContext.currentProxy())。
  2. 參數序列化慢:大對象轉 JSON 耗時可能超過業務邏輯,務必異步/采樣/過濾。
  3. 異常吞噬:finally 塊必須拋出原異常,否則事務回滾失效。

總結

Spring AOP @Around 切面是方法級耗時監控的「瑞士軍刀」:代碼極簡、零侵入、靈活擴展。生產環境建議 **「切面采集 + 異步日誌 + 指標導出 + 分布式追蹤」** 組合拳,逐步替代手工埋點,實現全鏈路可觀測。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved