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