更新時間:2024-06-07 07:16:38作者:佚名
0 簡介
使用java.lang.進行動態操作是Java SE 5的新特性,它將Java函數從本地代碼中解放出來,讓Java代碼能夠自己解決問題。有了它,開發人員可以構建獨立于應用程序的代理,來監控和協助運行在JVM上的程序,甚至可以替換和修改某些類的定義。有了該功能,開發人員可以實現更加靈活的運行時虛擬機監控和Java類操作。該功能實際上提供了一種虛擬機級別支持的AOP實現方法,開發人員可以在不升級或更改JDK的情況下實現某些AOP功能。
在Java SE 6中,包被賦予了更強大的功能:后啟動、本地代碼、動態變化等等,這些改變意味著Java擁有了更強的動態控制和解釋能力,使得Java語言變得更加靈活多變。
在Java SE6中,最大的改變使得運行時成為可能。在Java SE 5中,需要在運行前使用命令行參數或者系統參數設置代理類。在實際運行中,在虛擬機初始化的時候(大多數Java類庫加載之前),就會啟動代理類的設置,并在虛擬機中設置一個回調函數來檢測具體類的加載情況并完成實際的工作。但在很多實際情況下,我們沒有辦法在虛擬機啟動的時候就為其設置代理,這其實限制了應用。Java SE 6的新特性改變了這種情況,通過Java Tool API中的方法,我們可以很方便地在運行過程中動態地設置加載代理類,達到目的。
此外,接口訪問也是Java SE 6的一個全新特性,它使得以前不可能實現的功能——接口訪問可以在Java SE 6中通過一個或者一系列的添加來完成。
最后,Java SE 6 中添加了動態添加類路徑的功能。所有這些新特性使得包的功能更加強大,從而使 Java 語言本身更加強大。
1 基本功能及使用方法
JVMTI(Java Tool)是Java虛擬機為JVM相關工具提供的一套原生編程接口。JVMTI在Java SE 5中被引入,整合并取代了之前使用的Java(JVMPI)和Java Debug(JVMDI)。在Java SE 6中instrument是什么意思,JVMPI和JVMDI已經消失。JVMTI提供了一套“代理”程序機制,可以支持第三方工具程序以代理的方式連接并訪問JVM,并利用JVMTI提供的豐富編程接口完成很多JVM相關的功能。
其實java.lang.包的實現就是基于這種機制的:在實現中有一個JVMTI代理,通過調用JVMTI中Java類相關的函數來完成Java類的動態操作。除了上述函數之外,JVMTI還在虛擬機內存管理、線程控制、方法和變量操作等方面提供了大量有價值的函數。
1.1 VM 預啟動設置
最大的作用就是可以動態的改變和操作類定義,在Java SE 5以及后續版本中,開發者可以在普通的Java程序(有main函數的Java類)運行時,通過--參數指定具體的jar文件(包含代理)來啟動代理程序。
功能比較強大,可以批量轉換很多類別。
1.2 VM啟動后動態
在Java SE 5中,開發者只能發揮自己的想象力,所能做的事情僅限于在main函數執行之前,這種方式有一定的局限性。
Java SE 6 在Java SE 5的基礎上對此情況進行了改進,開發者可以在main函數開始執行后,啟動自己的程序。
在 Java SE 6 中,有一種名為“手拉手運行”的方法,可以在主函數開始運行后運行。就像函數一樣,開發人員可以編寫一個包含“”函數的 Java 類:
類似地,[1] 的優先級高于 [2],將首先執行。與函數一樣,開發人員可以對類進行各種操作。和 Inst 的用法相同。
與“-Class”類似,開發人員必須在文件中設置“Agent-Class”來指定包含該功能的類。
但不同的是,它需要在main函數開始運行之后才啟動,這樣的時機該如何確定,又該如何實現這樣的功能呢?
在 Java SE 6 的文檔中,開發者可能無法在與 java.lang. 包相關的文檔部分看到清晰的介紹,更不用說具體的應用示例了。然而,在 Java SE 6 的眾多新特性中,卻有一個不顯眼的地方透露了用法。這就是 Java SE 6 中提供的 API。
該API并非標準的Java API,而是Sun提供的用于將代理工具程序“附加”到目標JVM的擴展API,通過它,開發人員可以方便地監控JVM并運行附加的代理程序。
1.3 本地方法
在JDK 1.5版本中,沒有辦法處理Java本機方法()留學之路,而在Java標準JVMTI下也沒有辦法改變它,這使替換本機方法變得非常困難。一個更直接簡單的想法是在啟動時替換本機代碼所在的動態鏈接庫——但這本質上是靜態替換,而不是動態替換。而且,這可能需要編譯大量的動態鏈接庫——比如我們有三個本機函數,假設每一個都需要替換,而不同的應用程序可能需要不同的組合。如果我們將三個函數都編譯在同一個動態鏈接庫中,則將需要多達8個不同的動態鏈接庫才能滿足需求。當然instrument是什么意思,我們也可以獨立編譯它們,這也需要6個動態鏈接庫——無論如何,這種繁瑣的方法是不可接受的。
在Java SE 6中,為了解決一些問題,提出了一種新的代碼解析方式作為原有解析方式的補充。也就是說,在新版本的java.lang.包中,我們多了一種代碼方法——。
假設我們有一個名為 的函數,在執行過程中,需要將其指向另外一個函數(需要注意的是,在目前的標準 JVMTI 下,除了函數名不同,其他都需要保持一致)。例如我們的 Java 代碼為:
是不是很有趣呢?所以如果我們要做類似的工作,一個好的建議是先用Java寫一個接口,用javah工具生成一個c文件,看看它實際解析的函數名是什么,這樣就可以避免一些不必要的麻煩。
另一個事實是,與我們想象的不一樣,對于兩個或更多個,虛擬機不會進行更多解析;它不會嘗試刪除一個,然后組裝函數接口。它會且只進行兩次解析。
總之,新的方式改變了Java中代碼不能動態改變的弊端。目前使用JNI編寫代碼也是Java應用中很重要的一個部分,所以它的動態性就意味著整個Java都可以動態改變——現在我們的代碼可以使用plus來動態改變函數指針了。如上所說,如果找不到,虛擬機就會嘗試做標準分析,這就給我們提供了一種動態替換代碼的方法。我們可以把很多不同的函數編譯成一個動態鏈接庫,通過封裝的功能,讓函數像Java函數一樣動態地改變和替換。當然現在還是有一些限制的,比如不同的會各有各的,也就是各自負責他替換的所有類,而不是某個具體的類——所以這個粒度可能不夠精確。
1.4 / 動態添加
我們知道,通過設置系統參數或者虛擬機啟動參數,我們可以設置虛擬機運行時啟動時的一個類加載路徑(-)和類加載路徑(-cp),當然運行后我們無法替換它。但是,有時候我們需要加載一些jar包進去,就不能應用上面兩種方法了;或者需要在虛擬機啟動后再加載一些jar包進去。在Java SE 6中,我們可以這樣做。
要實現這幾點其實很簡單,首先我們還是需要確認虛擬機已經支持這個功能,然后添加所需的/,我們可以在 our 中使用 / 來完成這個任務。
同時我們可以注意到,在代理中添加Boot-Class-Path,其實可以在動態加載代理的同時,添加自己的引導類路徑。當然,在Java代碼中可以更動態、更方便、更智能地完成——我們可以很方便地添加判斷和選擇組件。
這里我們還需要注意幾點:
首先,我們添加的jar文件中不應該包含任何與系統相關的同名類,否則,一切都將變得不可預測——這不是一個工程師想要的結果,對吧?
其次我們要注意虛擬機的工作方式,它會把解析的結果記錄下來,比如我們曾經要求讀取某個類,但是失敗了,它就會記住這件事,即使我們后面動態添加了一個包含這個類的jar,它還是會認為我們無法解析這個類,會報上次一樣的錯誤。
再次,我們知道Java語言中有一個系統參數“java.class.path”,這個參數記錄了我們當前的,但是雖然我們用這兩個函數來實際改變實際的,但是對這個本身不會產生任何影響。
在 包中我們可以發現一個很有意思的東西,Sun 的設計者告訴我們這個函數其實依賴于一個方法——這是一個非 函數,所以我們不建議直接使用它(使用反射等)。其實包中的兩個函數已經可以很好的解決我們的問題了。
1.5 META-INF/.MF 列表
以下是代理 jar 文件的列表:
推薦公眾賬號: