Java:Java代理如何影響你的代碼
在構(gòu)建可擴展的服務(wù)器端應(yīng)用程序時,我們會花費大量時間思考如何在生產(chǎn)中監(jiān)控、操作和更新代碼。一種新的工具已經(jīng)發(fā)展起來,可以幫助Java開發(fā)人員做到這一點。它們中的許多都是建立在外部代碼可以在運行時與JVM集成的最強大的方式之一Java代理上的。
代理是操作系統(tǒng)本機或Java庫(我們將在下面描述它們的區(qū)別),JVM為這些庫提供了普通應(yīng)用程序代碼無法使用的功能。為了了解這些工具的基礎(chǔ)性,讓我們看看我們使用的一些依賴它們的工具:
l 探查器使用Java代理修改目標(biāo)框架的代碼,以注入收集性能指標(biāo)的新代碼。這包括獨立服務(wù)或托管服務(wù),如NewRelic或YourKit。
l JRebel通過構(gòu)建一種技術(shù)將其提升到了一個新的水平,該技術(shù)可以在運行時提供平滑的類熱交換,而無需重新啟動JVM。
l Play框架使用Java代理在運行時實現(xiàn)類的熱交換。
l 在Harness,我們利用JVM向本機代理提供的低級功能來顯示導(dǎo)致錯誤的實際源代碼和變量值。
Java代理
Java代理是.jar文件,它定義了一個特殊的premain靜態(tài)函數(shù),JVM將在調(diào)用應(yīng)用程序的主函數(shù)之前調(diào)用該函數(shù)。神奇的部分來自Instrumentation對象,它作為參數(shù)由主機JVM傳遞給這個函數(shù)。通過抓住這個對象,代理的代碼(在其他方面表現(xiàn)為根類加載器加載的任何Java代碼)可以做一些非常強大的事情。
他們做什么
給予代理的最強大的能力是在運行時類中動態(tài)重寫目標(biāo)類的方法內(nèi)容的能力(字段結(jié)構(gòu)是不可變的)。這個過程稱為字節(jié)碼插裝,它使代理能夠在代碼運行時重寫方法的內(nèi)容。
一些例子包括添加對特定方法的調(diào)用來分析性能(例如end time–start time)或記錄參數(shù)值(例如傳遞給servlet的URL)。另一個例子是在不重啟JVM的情況下重新加載一個新版本的類,就像JRebel所做的那樣。

這是怎么做到的
對于代理來說,修改代碼或加載的類實際上觸發(fā)了JVM重新加載類的過程,其中類的字節(jié)碼被替換為新版本。這要求代理能夠向JVM提供可驗證的新字節(jié)碼(即符合JVM規(guī)范)。不幸的是,在運行時生成正確的字節(jié)碼并不簡單——有很多需求和邊緣情況。為此,代理通常使用庫來讀寫字節(jié)碼。該庫使他們能夠?qū)F(xiàn)有類的字節(jié)碼加載到一個類似DOM的結(jié)構(gòu)中,通過添加諸如分析調(diào)用之類的東西來修改它,然后將DOM保存回原始字節(jié)碼。
?
ASM是一個流行的庫,它提供了一個通用的Java字節(jié)碼操作和分析框架。這是最流行的字節(jié)碼操作庫之一,可以用來修改現(xiàn)有的類或者直接以二進制形式動態(tài)生成類。它非常流行,以至于Sun的一些內(nèi)部代碼實際上使用它來解析Java中的字節(jié)碼。
最后的想法
了解什么是代理以及它們是如何構(gòu)建的是有好處的,因為即使你最終沒有編寫一個代理,你今天也可能會依賴其中的一個或多個來驅(qū)動你的應(yīng)用程序。