您好,登錄后才能下訂單哦!
深入淺析Java中的 classloader與namespace?針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
Java classloader和namespace詳細介紹
Java虛擬機通過裝載、連接和初始化一個JAVA類型,使該類型可以被正在運行的JAVA程序所使用。其中,裝載就是把二進制形式的JAVA類型讀入JAVA虛擬機中。連接就是把這種已經(jīng)讀入虛擬機的二進制形式的類型數(shù)據(jù)合并到虛擬機的運行時狀態(tài)中去。連接階段分為三個步驟-驗證、準備和解析。驗證確保了JAVA類型數(shù)據(jù)格式正確并適于JAVA虛擬機使用。準備負責(zé)為該類分配它所需的內(nèi)存,比如為它的類變量分配內(nèi)存。解析把常量池中的符號引用轉(zhuǎn)換為直接引用,如內(nèi)存地址指針。在初始化期間,激活類的靜態(tài)變量的初始化代碼和靜態(tài)代碼塊。
裝載步驟的最終產(chǎn)品是一個被裝載類型的Class類的實例對象,它成為JAVA程序與內(nèi)部數(shù)據(jù)結(jié)構(gòu)之間的接口。對于每一個被裝載的類型,虛擬機都會相應(yīng)地為它創(chuàng)建一個Class類的實例。
1 類裝載器的安全作用
JAVA類裝載器在JAVA安全體系結(jié)構(gòu)中起著最關(guān)重要的作用,是JAVA安全沙箱的第一道防線。類裝載器體系結(jié)構(gòu)在三個方面對JAVA的沙箱起作用:
1) 它防止惡意代碼去干涉善意的代碼
2) 它守護了被信任的類庫的邊界
3) 它將代碼歸入某類(稱為保護域),該類確定了代碼可以進行哪些操作。
類裝載器體系結(jié)構(gòu)可以防止惡意代碼去干涉善意的代碼,這是通過為不同的類裝載器裝入的類提供不同的命名空間來實現(xiàn)的。
2雙親委派模型
JAVA虛擬機規(guī)范定義了兩種類型的類裝載器-啟動類裝載器和用戶自定義類裝載器,啟動類裝載器是JAVA虛擬機實現(xiàn)的一部分,通過繼承ClassLoader類,用戶可以創(chuàng)建自定義的類裝載器來完成特定要求的加載。JAVA虛擬機已經(jīng)創(chuàng)建了2個自定義類裝載器-擴展類裝載器和系統(tǒng)類裝載器。
每一個用戶自定義的類裝載器在創(chuàng)建時被分配一個“雙親”parent類裝載器。如果沒有顯示地傳遞一個雙親類裝載器給用戶自定義的類裝載器的構(gòu)造方法,系統(tǒng)類裝載器就默認被指定為雙親。如果傳遞到構(gòu)造方法的是一個已有的用戶自定義類裝載器的引用,該用戶自定義類裝載器就作為雙親;如果向構(gòu)造方法傳遞了null,啟動類裝載器就是雙親。
啟動類裝載器Bootstrap Classloader:它是JAVA虛擬機實現(xiàn)的一部分,是c/c++實現(xiàn)的,它沒有雙親。啟動類裝載器裝載JAVA核心庫代碼。
擴展類裝載器Extension Classloader:繼承自URLClassLoader,初始化向構(gòu)造方法傳遞了null,所以雙親是Bootstrap Classloaser。它從java.ext.dirs擴展目錄中裝載代碼。
系統(tǒng)類裝載器Application Classloader:繼承自URLClassLoader,雙親是Extension Classloaser。它從CLASSPATH路徑中裝載應(yīng)用程序代碼。
其中,網(wǎng)絡(luò)類裝載器URLClassLoader是JAVA庫提供的一個類裝載器,用來從網(wǎng)絡(luò)其他位置裝載類。
雙親孩子類裝載器委派鏈
在雙親委派模型下,當(dāng)一個裝載器被請求裝載某個類時,它首先委托自己的雙親parent去裝載,若parent能裝載,則返回這個類所對應(yīng)的Class對象,若parent不能裝載,則由parent的請求者去裝載。
現(xiàn)在假設(shè)要求Cindy去裝載一個名為java.io.FileReader的類型。Cindy第一件事情就是去找Mom來裝載那個類型;Mom所做的第一件事情就是去找Grandma來裝載那個類型;而Grandma首先去找啟動類裝載器去裝載。在這個例子中,啟動類裝載器可以裝載那個類型,它就返回代表java.io.FileReader的Class實例給Grandma。Grandma傳遞該Class的引用 Mom,Mom再回傳給Cindy,Cindy返回給程序。
在此模型下,啟動類裝載器可以搶在擴展類裝載器之前去裝載類,而擴展類裝載器可以搶在系統(tǒng)類裝載器之前去裝載那個類,系統(tǒng)類裝載器又可以搶在網(wǎng)絡(luò)類裝載器之前去裝載它。這樣,使用雙親-孩子委派鏈的方式,啟動類裝載器會在最可信的類庫-核心Java API-中首先檢查每個被裝載的類型,然后,才依次到擴展路徑、系統(tǒng)類路徑中檢查被裝載的類型文件。用這種方法,類裝載器的體系結(jié)構(gòu)就可以防止不可靠的代碼用它們自己的版本來替代可以信任的類。
3命名空間
由不同的類裝載器裝載的類將被放在虛擬機內(nèi)部的不同命名空間。命名空間由一系列唯一的名稱組成,每一個被裝載的類有一個名字。JAVA虛擬機為每一個類裝載器維護一個名字空間。例如,一旦JAVA虛擬機將一個名為Volcano的類裝入一個特定的命名空間,它就不能再裝載名為Valcano的其他類到相同的命名空間了??梢园讯鄠€Valcano類裝入一個JAVA虛擬機中,因為可以通過創(chuàng)建多個類裝載器從而在一個JAVA應(yīng)用程序中創(chuàng)建多個命名空間。
1) 初始類裝載器/ 定義類裝載器
命名空間有助于安全的實現(xiàn),因為你可以有效地在裝入了不同命名空間的類之間設(shè)置一個防護罩。在JAVA虛擬機中,在同一個命名空間內(nèi)的類可以直接進行交互,而不同的命名空間中的類甚至不能覺察彼此的存在,除非顯示地提供了允許它們進行交互的機制,如獲取Class對象的引用后使用反射來訪問。
如果要求某個類裝載器去裝載一個類型,但是卻返回了其他類裝載器裝載的類型,這種裝載器被稱為是那個類型的初始類裝載器 ;而實際裝載那個類型的類裝載器被稱為該類型的定義類裝載器 。任何被要求裝載類型,并且能夠返回Class實例的引用代表這個類型的類裝載器,都是這個類型的初始類裝載器。在上面的一個例子中,java.io.FileReader定義類裝載器是啟動類裝載器,Cindy、Mom、Grandma、啟動類裝載器都是初始類裝載器。
虛擬機會為每一個類裝載器維護一張列表,列表中是已經(jīng)被請求過的類型的名字。這些列表包含了每一個類裝載器被標(biāo)記為初始類裝載器的類型,它們代表了每一個類裝載器的命名空間。虛擬機總是會在調(diào)用loadClass()之前檢查這個內(nèi)部列表,如果這個類裝載器已經(jīng)被標(biāo)記為是這個具有該全限定名的類型的初始類裝載器,就會返回表示這個類型的Class實例,這樣,虛擬機永遠不會自動在同一個用戶自定義類裝載器上調(diào)用同一個名字的類型兩次。
2) 命名空間的類型共享
前面提到過只有同一個命名空間內(nèi)的類才可以直接進行交互,但是我們經(jīng)常在由用戶自定義類裝載器定義的類型中直接使用Java API類,這不是矛盾了嗎?這是類型共享 原因-如果某個類裝載器把類型裝載的任務(wù)委派給另外一個類裝載器,而后者定義了這個類型,那么被委派的類裝載器裝載的這個類型,在所有被標(biāo)記為該類型的初始類裝載器的命名空間中共享。
例如上面的例子中,Cindy可以共享Mon、Grandma、啟動類裝載器的命名空間中的類型,Kenny也可以共享 Mon、Grandma、啟動類裝載器的 命名空間中的 類型,但是Cindy和Kenny的命名空間不能共享。
3) 運行時包
每個類裝載器都有自己的命名空間,其中維護著由它裝載的類型。所以一個JAVA程序可以多次裝載具有同一個全限定名的多個類型。這樣一個類型的全限定名就不足以確定在一個JAVA虛擬機中的唯一性。因此,當(dāng)多個類裝載器都裝載了同名的類型時,為了唯一表示該類型,還要在類型名稱前加上裝載該類型的類裝載器來表示-[classloader class]。
在允許兩個類型之間對包內(nèi)可見的成員進行訪問前,虛擬機不但要確定這個兩個類型屬于同一個包,還必須確認它們屬于同一個運行時包-它們必須有同一個類裝載器裝載的。這樣,java.lang.Virus和來自核心的java.lang的類不屬于同一個運行時包,java.lang.Virus就不能訪問JAVA API的java.lang包中的包內(nèi)可見的成員。
4自定義類裝載器
JAVA類型要么由啟動類裝載器裝載,要么通過用戶自定義的類裝載器裝載。啟動類裝載器是虛擬機實現(xiàn)的一部分,它以與實現(xiàn)無關(guān)的方式裝載類型,JAVA提供了抽象類java.lang.ClassLoader,用戶自定義的類裝載器是類ClassLoader的子類實例,它以定制的方式裝載類。所有用戶自定義類裝載器都實例化自ClassLoader的子類。
下面提供一個簡單的用戶自定義類裝載器。
import java.io.*; public class UserDefinedClassLoader extends ClassLoader { private String directory = "d:/classes/"; private String extensionType = ".class"; public UserDefinedClassLoader() { super(); // this set the parent as the AppClassLoader by default } public UserDefinedClassLoader( ClassLoader parent ) { super( parent ); } public Class findClass( String name ) { byte[] data = loadClassData( name ); return defineClass( name, data, 0, data.length ); } private byte[] loadClassData( String name ) { byte[] data = null; try { FileInputStream in = new FileInputStream( new File( directory + name.replace( '.', '/') + extensionType ) ); ByteArrayOutputStream out = new ByteArrayOutputStream(); int ch = 0; while( ( ch = in.read() ) != -1 ) { out.write( ch ); } data = out.toByteArray(); } catch ( IOException e ) { e.printStackTrace(); } return data; } }
public class Valcano { static { System.out.println("Valcano Class Initialized"); } public Valcano() { } } public class ClassLoaderTest { public static void main( String[] args ) { try { UserDefinedClassLoader userLoader = new UserDefinedClassLoader(); Class valcanoClass1 = userLoader.loadClass( "Valcano" ); URL url = new URL("file:/d:/classes/" ); ClassLoader urlLoader = new URLClassLoader( new URL[] { url } ); Class valcanoClass2 = urlLoader.loadClass( "Valcano" ); System.out.println( "valcanoClass1 classloaer = " + valcanoClass1.getClassLoader() ); System.out.println( "valcanoClass2 classloaer = " + valcanoClass2.getClassLoader() ); System.out.println( "valcanoClass1 = valcanoClass2 ? " + ( valcanoClass1 == valcanoClass2 ) ); } catch( Exception e ) { e.printStackTrace(); } } }
輸出結(jié)果:
valcanoClass1 classloaer = UserDefinedClassLoader@1fb8ee3 valcanoClass2 classloaer = java.NET.URLClassLoader@14318bb valcanoClass1 = valcanoClass2 ? false
我們可以看到,有兩個不同的Valcano的Class實例被加載到同一個虛擬機中。
另外我們看到Valcano類靜態(tài)初始化語句沒有被執(zhí)行,意味著類沒有被初始化,這是因為JAVA中只有當(dāng)類被主動使用時類型才會進行初始化。
關(guān)于深入淺析Java中的 classloader與namespace問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。