前言:為何傳統監控失效於生產環境
現代 Java 應用是複雜、多執行緒、分散式系統。Profiler 開銷過大,不適合生產/負測環境。手工插桩代碼分散、糾纏、難維護、難擴展。**系統監控是經典橫切關注點** — AspectJ 天生適合:定義切點匹配數百連接點,Advice 自動織入統計邏輯,模組化、可擴展、開銷可控。
本系列使用開源 **Glassbox Inspector** 框架核心代碼示範:以中等代碼量實現請求計數/耗時/最差案例、JDBC 連線/Statement 深度監控、JMX 即時公開、巢狀統計關聯 — **生產環境可用開銷**。
核心架構總覽

圖 1:多應用監控、合併統計、JMX Remote 公開、JConsole 視覺化

圖 2:切面與容器內應用交互 → 捕獲效能數據 → JMX Remote 標準暴露
**JMX 優勢**:標準 API、成熟工具 (JConsole、VisualVM)、Java 5 內建、企業容器標配。
第一步:Servlet 請求基礎監控
public aspect HttpServletMonitor {
public pointcut monitoredOperation(Object operation) :
execution(void HttpServlet.do*(..)) && this(operation); // 匹配 doGet/doPost/doPut...
void around(Object operation) : monitoredOperation(operation) {
long start = System.currentTimeMillis();
proceed(operation);
PerfStats stats = lookupStats(operation);
stats.recordExecution(start, System.currentTimeMillis());
}
// lookupStats 以 Servlet Class 為鍵,WeakIdentityHashMap 儲存
}
- 切點精準匹配所有 `HttpServlet.do*` 方法。
- `around` Advice:計時 → `proceed()` → 記錄統計。
- `PerfStats` 介面:`accumulatedTime`、`maxTime`、`count`、`reset()`。
- `PerfStatsFactory` 抽象統計物件建立,利於擴展。
第二步:JMX 公開 — 讓統計可視、可管
**可重用 `JmxManagement` 切面**:
- 標記介面 `ManagedBean` 定義 `getOperationName()`、`getMBean()`。
- `after() returning` 攔截 `ManagedBean` 建構子 → 自動向 `MBeanServer` 註冊。
- IOC 模式注入 `MBeanServer`,適合 Spring 容器管理。
**`StatsJmxManagement` 專用於統計**:
- `declare parents: PerfStats implements PerfStatsMBean` — **動態實作介面**,避免標準 MBean 需為每個實作類建立對應介面的僵化。
- 使用 Spring `InterfaceBasedMBeanInfoAssembler` 建立 `DynamicMBean`,以 `PerfStatsMBean` 為管理介面。
- JMX ObjectName 格式:`glassbox.inspector:operation="fully.qualified.ServletClassName"`。

圖 3:JConsole 即時顯示各 Servlet 執行次數、總耗時、最大耗時
第三步:資料庫深度監控 — JDBC 連線與 Statement
**設計模式:Worker Object (RequestContext)** — 匿名內部類封裝請求上下文,支援巢狀關聯、生命週期管理、統計快取。比 `ThreadLocal` 更模組化、型別安全。
抽象基礎 `AbstractRequestMonitor`
- `requestExecution(RequestContext)` / `inRequest(RequestContext)` 切點定義監控範圍。
- `after() returning` 攔截 `RequestContext` 建構子 → 建立父子關聯 (`setParent`),**實現巢狀統計樹** (Servlet → JDBC Connection → Statement)。
- `RequestContext.execute()` 模板方法:計時 → `doExecute()` (子類實作) → 記錄統計。
- `lookupStats()` 抽象方法:子類依上下文決定統計物件 (Servlet 看類別、JDBC 看 DataSource/URL/SQL)。
JdbcConnectionMonitor:連線效能與資料庫識別
- 切點:`DataSource.getConnection()`、`Driver.connect()`、`DriverManager.getConnection()`。
- 排除巢狀連線調用 (`cflowbelow`),避免重複計數。
- 關鍵挑戰:**DataSource 無標準取名方法** → 反射嘗試 `getDatabaseName`、`getUrl`、`getDescription` 等常見方法。
- `WeakIdentityHashMap` 關聯連線實例與資料庫名稱,**不依賴 `hashCode/equals`**,支援動態 Proxy/Wrapper 連線。
- 父請求 (Servlet) 統計物件持有各資料庫 `DatabaseStats` 容器。
JdbcStatementMonitor:SQL 執行效能與參數關聯
- 切點:`java.sql..*.execute*()` — 捕獲所有 Statement/PreparedStatement/CallableStatement 執行。
- 兩張弱身份映射表:
- `statementCreators: Statement -> Connection`:`after() returning` 攔截 `Connection.createStatement()` 等 12 種建立方法,**繞過 Wrapper 連線的 `getConnection()`**。
- `statementSql: Statement -> String`:`before` 切點捕獲動態 SQL 參數 (`args(sql)`);`after` 切點捕獲 `PreparedStatement` 建立時的 SQL。
- `stripAfterWhere(sql)` 移除 WHERE 子句後內容:**避免敏感數據洩漏、將同類查詢歸類統計**。
- `lookupStats()` 串聯查詢:Statement → Connection → 資料庫名稱 → 父 Servlet 統計 → `DatabaseStats` → `RequestStats(SQL)`。
- 防禦性編程:各層檢查 `null`,應用違背預期時不拋出異常。

圖 4:監控物件層次 — OperationStats (Servlet) → DatabaseStats (DataSource) → RequestStats (SQL)

圖 5:JConsole 深入檢視特定資料庫、特定 SQL 執行統計
巢狀 Servlet 監控升級
更新 `HttpServletMonitor` 繼承 `AbstractRequestMonitor`,使用 `RequestContext` Worker Object。`lookupStats()` 邏輯:
if (getParent() != null) {
// 巢狀操作:如 BannerServlet 被其他 Servlet include
return parentStats.getOperationStats(operation.getClass());
}
return lookupStats(operation.getClass()); // 頂層操作
實現 **同一請求內多 Servlet 調用的獨立統計** (如 Duke 書店所有頁面包含 BannerServlet)。
第一部分總結
- 建立核心監控基礎設施:Servlet、JDBC 連線/Statement、巢狀統計、JMX 公開。
- AspectJ 模組化優勢:精準切點、可擴展 Advice、關注點分離、零業務代碼侵入。
- Worker Object 模式優雅解決上下文傳遞與巢狀關聯。
- WeakIdentityHashMap 解決第三方 Proxy/Wrapper 物件身份追蹤難題。
- 代碼簡潔、開銷可控、生產環境可用。
第二部分將擴展:多應用隔離、Struts/Spring MVC/WebService 監控、錯誤追蹤、執行時動態開關、錯誤隔離層、LTW 部署、效能基準測試。敬請期待!