new的奧秘:Java中new關(guān)鍵字與類(lèi)加載器
原創(chuàng)【51CTO獨(dú)家特稿】在Java中使用new關(guān)鍵字創(chuàng)建對(duì)象變得很容易了,事實(shí)上,對(duì)這些事情你是不需要考慮的。需要訪(fǎng)問(wèn)一個(gè)文件嗎?只需要?jiǎng)?chuàng)建一個(gè)新的File實(shí)例:new File(“build.properties”),對(duì)于大多數(shù)Java開(kāi)發(fā)人員而言,這就是他們需要知道的一切,是不是很簡(jiǎn)單呢?!但當(dāng)你使用了多個(gè)類(lèi)加載器時(shí),問(wèn)題就不一樣了。
這是我一年多來(lái)的第一反應(yīng),我就是不想知道這些東西,但奇怪的是,類(lèi)加載器其實(shí)非常簡(jiǎn)單,大多數(shù)Java開(kāi)發(fā)人員都知道編譯時(shí)通過(guò)Java文件生成.class類(lèi)文件,然后由Java虛擬機(jī)(JVM)載入這些編譯后的類(lèi),這就是類(lèi)加載器最基本的功能,但是和線(xiàn)程一樣,問(wèn)題不是理解他們做什么,而是讓它們一起工作。
你聽(tīng)到過(guò)多少次“這是類(lèi)加載器的問(wèn)題”?我承認(rèn)我聽(tīng)到過(guò)很多次,我自己也說(shuō)過(guò)很多次。只要你的應(yīng)用程序中不止一個(gè)類(lèi)加載器,你不得不擔(dān)心哪些類(lèi)可以相互看到對(duì)方,這很容易成為一場(chǎng)噩夢(mèng)。有關(guān)類(lèi)加載器行為我將另外用一篇文章來(lái)說(shuō),現(xiàn)在我們還是回到new關(guān)鍵字吧。
當(dāng)你創(chuàng)建一個(gè)新對(duì)象時(shí),JVM首先加載類(lèi),當(dāng)你使用new時(shí)這是透明的,問(wèn)題是使用什么類(lèi)加載器?以及為什么要使用它?
設(shè)想一個(gè)Grails情景,我們有一套基于Gant的構(gòu)建系統(tǒng),載入構(gòu)建腳本并執(zhí)行它們,我們以實(shí)例化一個(gè)Jetty服務(wù)器并啟動(dòng)它作為示例,對(duì)象的創(chuàng)建順序是這樣的:
事實(shí)上,上圖展示的僅僅是一個(gè)簡(jiǎn)化的真實(shí)情況。
前3個(gè)類(lèi)都在我們將要調(diào)用的生成類(lèi)加載器的類(lèi)路徑下,因此是一次性將生成時(shí)需要用到的所有類(lèi)全部加載了,但Jetty的Server類(lèi)怎么加載呢?最重要的是要知道Server類(lèi)必須要通過(guò)加載Grails Web應(yīng)用程序相同的類(lèi)加載器加載,雖然你可以將你自己的類(lèi)加載器嵌入到服務(wù)器中,如果與加載Server的不一樣,將會(huì)出現(xiàn)可怕的類(lèi)加載器問(wèn)題。
考慮到這一點(diǎn),讓我們看看如果RunApp腳本使用new創(chuàng)建服務(wù)器實(shí)例會(huì)發(fā)生什么:
- def server = new org.mortbay.jetty.Server()
- ...
- server.start()
現(xiàn)在你應(yīng)該問(wèn)你自己“加載Server類(lèi)該使用什么類(lèi)加載器?”,這是一個(gè)關(guān)鍵問(wèn)題,因?yàn)樗鼪Q定了使用什么類(lèi)加載器加載整個(gè)Web應(yīng)用程序,也就決定了應(yīng)用程序的運(yùn)行時(shí)應(yīng)該使用和依賴(lài)的類(lèi)路徑,在這種情況下,無(wú)論使用什么類(lèi)加載器加載RunApp腳本,new操作符都會(huì)有效地授權(quán)給
- this.getClass().getClassLoader()
我們的例子是什么意思呢?它意味著生成類(lèi)加載器被用于加載Server類(lèi),因此也必須用于加載Web應(yīng)用程序類(lèi),換句話(huà)說(shuō),所有應(yīng)用程序的運(yùn)行時(shí)依賴(lài)必須包括在生成類(lèi)加載器中。你可能會(huì)問(wèn),這樣會(huì)不會(huì)有問(wèn)題?回答是有一個(gè)潛在的問(wèn)題和一個(gè)實(shí)際的問(wèn)題。
潛在的問(wèn)題是類(lèi)沖突,如果Web應(yīng)用程序依賴(lài)一個(gè)已經(jīng)存在于生成系統(tǒng)中不同版本的庫(kù)會(huì)怎么樣?如果所有Apache XML API庫(kù)都在類(lèi)路徑下,這是一個(gè)特殊的問(wèn)題,絕對(duì)會(huì)導(dǎo)致大破壞。
實(shí)際的問(wèn)題是在類(lèi)路徑中JAR文件越多,JVM尋找類(lèi)的時(shí)間就越長(zhǎng),這意味著啟動(dòng)時(shí)間就越長(zhǎng),這也是OSGi設(shè)計(jì)要解決的問(wèn)題之一,為什么要將JAR放在生成類(lèi)路徑下呢?生成時(shí)本身是不需要它們的。
解決辦法是確定類(lèi)加載器的邊界,使用映射實(shí)例化你的對(duì)象:
- def runtimeClassLoader = new URLClassLoader(...)
- def server = runtimeClassLoader.loadClass("org.mortbay.jetty.Server").newInstance()
- ...
- server.start()