版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
JVM中的類加載器一個JVM中會有一個自帶的原生類加載器(primordialclassloader),所謂原生的意思確實(shí)是,若是那個jvm是用C寫的,那么那個類加載器也是用C寫好的。另一種確實(shí)是用戶自己寫的classloader。如以下圖所示。JVM中的類加載器所有通過原生加載器加載的類,JVM以為是可信任的;而通過其他加載器加載的,不是可信任的。由于一個jvm往往有多個加載器對象(非原生的),因此要記住某個類A是哪個加載器加載的,如此,那個類A引用到B的時候,JVM還會讓加載A的加載器去加載B。而不是隨機(jī)分派一個。一個加載器類能夠有多個實(shí)例,而一個JVM中有能夠有多個加載器類,因此一個JVM中會有很多個加載器實(shí)例。這些加載器都是用java代碼編寫的,他們都有一個命名空間。另外要注意的是,不同加載器加載的類之間是相互不可見的。除非你明確許諾這么干,那個機(jī)制有平安方面的重要意義。關(guān)鍵字:虛擬機(jī)引導(dǎo)當(dāng)JVM(Java虛擬機(jī))啟動時,會形成由三個類加載器組成的初始類加載器層次結(jié)構(gòu):
bootstrapclassloader
|
extensionclassloader
|
systemclassloader
bootstrapclassloader-引導(dǎo)(也稱為原始)類加載器,它負(fù)責(zé)加載Java的核心類。在Sun的JVM中,在執(zhí)行java的命令中利用-Xbootclasspath選項(xiàng)或利用-D選項(xiàng)指定sun.boot.class.path系統(tǒng)屬性值能夠指定附加的類。那個加載器的是超級特殊的,它事實(shí)上不是java.lang.ClassLoader的子類,而是由JVM自身實(shí)現(xiàn)的。大伙兒能夠通過執(zhí)行以下代碼來取得bootstrapclassloader加載了那些核心類庫:
Java代碼
1.
URL[]
urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
2.
for
(int
i
=
0;
i
<
urls.length;
i++)
{
3.
System.out.println(urls.toExternalForm());
4.
}
在我的運(yùn)算機(jī)上的結(jié)果為:
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
file:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
file:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
file:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
file:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
file:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
file:/C:/j2sdk1.4.1_01/jre/classes
這時大伙兒明白了什么緣故咱們不需要在系統(tǒng)屬性CLASSPATH中指定這些類庫了吧,因?yàn)镴VM在啟動的時候就自動加載它們了。
extensionclassloader-擴(kuò)展類加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或由java.ext.dirs系統(tǒng)屬性指定的)中JAR的類包。這為引入除Java核心類之外的新功能提供了一個標(biāo)準(zhǔn)機(jī)制。因?yàn)槟S的擴(kuò)展目錄對所有從同一個JRE中啟動的JVM都是通用的,因此放入那個目錄的JAR類包對所有的JVM和systemclassloader都是可見的。在那個實(shí)例上挪用方式getParent()老是返回空值null,因?yàn)橐龑?dǎo)加載器bootstrapclassloader不是一個真正的ClassLoader實(shí)例。因此當(dāng)大伙兒執(zhí)行以下代碼時:
Java代碼
1.
System.out.println(System.getProperty("java.ext.dirs"));
2.
ClassLoader
extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
3.
System.out.println("the
parent
of
extension
classloader
:
"+extensionClassloader.getParent());
結(jié)果為:
C:\j2sdk1.4.1_01\jre\lib\ext
theparentofextensionclassloader:null
extensionclassloader是systemclassloader的parent,而bootstrapclassloader是extensionclassloader的parent,但它不是一個實(shí)際的classloader,因此為null。
systemclassloader-系統(tǒng)(也稱為應(yīng)用)類加載器,它負(fù)責(zé)在JVM被啟動時,加載來自在命令java中的-classpath或java.class.path系統(tǒng)屬性或CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類途徑。總能通過靜態(tài)方式ClassLoader.getSystemClassLoader()找到該類加載器。若是沒有專門指定,那么用戶自概念的任何類加載器都將該類加載器作為它的父加載器。執(zhí)行以下代碼即可取得:
System.out.println(System.getProperty("java.class.path"));
輸出結(jié)果那么為用戶在系統(tǒng)屬性里面設(shè)置的CLASSPATH。
classloader加載類用的是通盤負(fù)責(zé)委托機(jī)制。所謂通盤負(fù)責(zé),即是當(dāng)一個classloader加載一個Class的時候,那個Class所依托的和引用的所有Class也由那個classloader負(fù)責(zé)載入,除非是顯式的利用另外一個classloader載入;委托機(jī)制那么是先讓parent(父)類加載器(而不是super,它與parentclassloader類不是繼承關(guān)系)尋覓,只有在parent找不到的時候才從自己的類途徑中去尋覓。另外類加載還采納了cache機(jī)制,也確實(shí)是若是cache中保留了那個Class就直接返回它,若是沒有才從文件中讀取和轉(zhuǎn)換成Class,并存入cache,這確實(shí)是什么緣故咱們修改了Class可是必需從頭啟動JVM才能生效的緣故。
每一個ClassLoader加載Class的進(jìn)程是:
1.檢測此Class是不是載入過(即在cache中是不是有此Class),若是有到8,若是沒有到2
2.若是parentclassloader不存在(沒有parent,那parent必然是bootstrapclassloader了),到4
3.請求parentclassloader載入,若是成功到8,不成功到5
4.請求jvm從bootstrapclassloader中載入,若是成功到8
5.尋覓Class文件(從與此classloader相關(guān)的類途徑中尋覓)。若是找不到那么到7.
6.從文件中載入Class,到8.
7.拋出ClassNotFoundException.
8.返回Class.
其中5.6步咱們能夠通過覆蓋ClassLoader的findClass方式來實(shí)現(xiàn)自己的載入策略。乃至覆蓋loadClass方式來實(shí)現(xiàn)自己的載入進(jìn)程。
類加載器的順序是:
先是bootstrapclassloader,然后是extensionclassloader,最后才是systemclassloader。大伙兒會發(fā)覺加載的Class越是重要的越在靠前面。如此做的緣故是出于平安性的考慮,試想若是systemclassloader“親自”加載了一個具有破壞性的“java.lang.System”類的后果吧。這種委托機(jī)制保證了用戶即便具有一個如此的類,也把它加入到了類途徑中,可是它永久可不能被載入,因?yàn)槟莻€類老是由bootstrapclassloader來加載的。大伙兒能夠執(zhí)行一下以下的代碼:
System.out.println(System.class.getClassLoader());
將會看到結(jié)果是null,這就說明java.lang.System是由bootstrapclassloader加載的,因?yàn)閎ootstrapclassloader不是一個真正的ClassLoader實(shí)例,而是由JVM實(shí)現(xiàn)的,正如前面已經(jīng)說過的。
下面就讓咱們來看看JVM是如何來為咱們來成立類加載器的結(jié)構(gòu)的:
sun.misc.Launcher,顧名思義,當(dāng)你執(zhí)行java命令的時候,JVM會先利用bootstrapclassloader載入并初始化一個Launcher,執(zhí)行下來代碼:
Java代碼
1.
System.out.println("the
Launcher's
classloader
is
"+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
2.
:
theLauncher'sclassloaderisnull(因?yàn)槭怯胋ootstrapclassloader加載,因此classloader為null)
Launcher會依照系統(tǒng)和命令設(shè)定初始化好classloader結(jié)構(gòu),JVM就用它來取得extensionclassloader和systemclassloader,并載入所有的需要載入的Class,最后執(zhí)行java命令指定的帶有靜態(tài)的main方式的Class。extensionclassloader事實(shí)上是sun.misc.Launcher$ExtClassLoader類的一個實(shí)例,systemclassloader事實(shí)上是sun.misc.Launcher$AppClassLoader類的一個實(shí)例。而且都是.URLClassLoader的子類。
讓咱們來看看Launcher初試化的進(jìn)程的部份代碼。
Launcher的部份代碼:
Java代碼
1.
public
class
Launcher
{
2.
public
Launcher()
{
3.
ExtClassLoader
extclassloader;
4.
try
{
5.
//初始化extension
classloader
6.
extclassloader
=
ExtClassLoader.getExtClassLoader();
7.
}
catch(IOException
ioexception)
{
8.
throw
new
InternalError("Could
not
create
extension
class
loader");
9.
}
10.
try
{
11.
//初始化system
classloader,parent是extension
classloader
12.
loader
=
AppClassLoader.getAppClassLoader(extclassloader);
13.
}
catch(IOException
ioexception1)
{
14.
throw
new
InternalError("Could
not
create
application
class
loader");
15.
}
16.
//將system
classloader設(shè)置成當(dāng)前線程的context
classloader(將在后面加以介紹)
17.
Thread.currentThread().setContextClassLoader(loader);
18.
......
19.
}
20.
public
ClassLoader
getClassLoader()
{
21.
//返回system
classloader
22.
return
loader;
23.
}
24.
}
extensionclassloader的部份代碼:
Java代碼
1.
static
class
Launcher$ExtClassLoader
extends
URLClassLoader
{
2.
3.
public
static
Launcher$ExtClassLoader
getExtClassLoader()
4.
throws
IOException
5.
{
6.
File
afile[]
=
getExtDirs();
7.
return
(Launcher$ExtClassLoader)AccessController.doPrivileged(new
Launcher$1(afile));
8.
}
9.
private
static
File[]
getExtDirs()
{
10.
//取得系統(tǒng)屬性“java.ext.dirs”
11.
String
s
=
System.getProperty("java.ext.dirs");
12.
File
afile[];
13.
if(s
!=
null)
{
14.
StringTokenizer
stringtokenizer
=
new
StringTokenizer(s,
File.pathSeparator);
15.
int
i
=
stringtokenizer.countTokens();
16.
afile
=
new
File;
17.
for(int
j
=
0;
j
<
i;
j++)
18.
afile[j]
=
new
File(stringtokenizer.nextToken());
19.
20.
}
else
{
21.
afile
=
new
File[0];
22.
}
23.
return
afile;
24.
}
25.
}
systemclassloader的部份代碼:
Java代碼
1.
static
class
Launcher$AppClassLoader
extends
URLClassLoader
2.
{
3.
4.
public
static
ClassLoader
getAppClassLoader(ClassLoader
classloader)
5.
throws
IOException
6.
{
7.
//取得系統(tǒng)屬性“java.class.path”
8.
String
s
=
System.getProperty("java.class.path");
9.
File
afile[]
=
s
!=
null
?
Launcher.access$200(s)
:
new
File[0];
10.
return
(Launcher$AppClassLoader)AccessController.doPrivileged(new
Launcher$2(s,
afile,
classloader));
11.
}
12.
}
看了源代碼大伙兒就清楚了吧,extensionclassloader是利用系統(tǒng)屬性“java.ext.dirs”設(shè)置類搜索途徑的,而且沒有parent。systemclassloader是利用系統(tǒng)屬性“java.class.path”設(shè)置類搜索途徑的,而且有一個parentclassloader。Launcher初始化extensionclassloader,systemclassloader,并將systemclassloader設(shè)置成為contextclassloader,可是僅僅返回systemclassloader給JVM。
那個地址怎么又出來一個contextclassloader呢?它有什么用呢?咱們在成立一個線程Thread的時候,能夠?yàn)槟莻€線程通過setContextClassLoader方式來指定一個適合的classloader作為那個線程的contextclassloader,當(dāng)此線程運(yùn)行的時候,咱們能夠通過getContextClassLoader方式來取得此contextclassloader,就能夠夠用它來載入咱們所需要的Class。默許的是systemclassloader。利用那個特性,咱們能夠“打破”classloader委托機(jī)制了,父classloader能夠獲適當(dāng)前線程的contextclassloader,而那個contextclassloader能夠是它的子classloader或其他的classloader,那么父classloader就能夠夠從其取得所需的Class,這就打破了只能向父classloader請求的限制了。那個機(jī)制能夠知足當(dāng)咱們的classpath是在運(yùn)行時才確信,并由定制的classloader加載的時候,由systemclassloader(即在jvmclasspath中)加載的class能夠通過contextclassloader取得定制的classloader并加載入特定的class(一般是抽象類和接口,定制的classloader中是其實(shí)現(xiàn)),例如web應(yīng)用中的servlet確實(shí)是用這種機(jī)制加載的.
好了,此刻咱們了解了classloader的結(jié)構(gòu)和工作原理,那么咱們?nèi)绾螌?shí)此刻運(yùn)行時的動態(tài)載入和更新呢?只要咱們能夠動態(tài)改變類搜索途徑和清除classloader的cache中已經(jīng)載入的Class就好了,有兩個方案,一是咱們繼承一個classloader,覆蓋loadclass方式,動態(tài)的尋覓Class文件并利用defineClass方式來;另一個那么超級簡單有效,只要從頭利用一個新的類搜索途徑來new一個classloader就好了,如此即更新了類搜索途徑以便來載入新的Class,也從頭生成了一個空白的cache(固然,類搜索途徑不必然必需更改)。噢,太好了,咱們幾乎不用做什么工作,URLClassLoader正是一個符合咱們要求的classloader!咱們能夠直接利用或繼承它就能夠夠了!
這是j2se1.4API的doc中URLClassLoader的兩個構(gòu)造器的描述:
URLClassLoader(URL[]urls)
ConstructsanewURLClassLoaderforthespecifiedURLsusingthedefaultdelegationparentClassLoader.
URLClassLoader(URL[]urls,ClassLoaderparent)
ConstructsanewURLClassLoaderforthegivenURLs.
其中URL[]urls確實(shí)是咱們要設(shè)置的類搜索途徑,parent確實(shí)是那個classloader的parentclassloader,默許的是systemclassloader。
好,此刻咱們能夠動態(tài)的載入Class了,如此咱們就能夠夠利用newInstance方式來取得一個Object。但咱們?nèi)绾螌⒋薕bject造型呢?能夠?qū)⒋薕bject造型成它本身的Class嗎?
第一讓咱們來分析一下java源文件的編譯,運(yùn)行吧!javac命令是挪用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方式來編譯:
Java代碼
1.
public
static
int
compile(String
as[]);
2.
3.
public
static
int
compile(String
as[],
PrintWriter
printwriter);
返回0表示編譯成功,字符串?dāng)?shù)組as那么是咱們用javac命令編譯時的參數(shù),以空格劃分。例如:
javac-classpathc:\foo\bar.jar;.-dc:\c:\Some.java
那么字符串?dāng)?shù)組as為{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\\Some.java"},若是帶有PrintWriter參數(shù),那么會把編譯信息出到那個指定的printWriter中。默許的輸出是System.err。
其中Main是由JVM利用Launcher初始化的systemclassloader載入的,依照通盤負(fù)責(zé)原那么,編譯器在解析那個java源文件時所發(fā)覺的它所依托和引用的所有Class也將由systemclassloader載入,若是systemclassloader不能載入某個Class時,編譯器將拋出一個“cannotresolvesymbol”錯誤。
因此第一編譯就通只是,也確實(shí)是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯誤或沒有把所需類庫放到CLASSPATH中,大伙兒必然常常看到那個“cannotresolvesymbol”那個編譯錯誤吧!
第二,確實(shí)是咱們把那個Class放到編譯途徑中,成功的進(jìn)行了編譯,然后在運(yùn)行的時候不把它放入到CLASSPATH中而利用咱們自己的classloader來動態(tài)載入那個Class,這時也會顯現(xiàn)“java.lang.NoClassDefFoundError”的違例,什么緣故呢?
咱們再來分析一下,第一挪用那個造型語句的可執(zhí)行的Class必然是由JVM利用Launcher初始化的systemclassloader載入的,依照通盤負(fù)責(zé)原那么,當(dāng)咱們進(jìn)行造型的時候,JVM也會利用systemclassloader來嘗試載入那個Class來對實(shí)例進(jìn)行造型,自然在systemclassloader尋覓不到那個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。
OK,此刻讓咱們來總結(jié)一下,java文件的編譯和Class的載入執(zhí)行,都是利用Launcher初始化的systemclassloader作為類載入器的,咱們無法動態(tài)的改變systemclassloader,更無法讓JVM利用咱們自己的classloader來替換systemclassloader,依照通盤負(fù)責(zé)原那么,就限制了編譯和運(yùn)行時,咱們無法直接顯式的利用一個systemclassloader尋覓不到的Class,即咱們只能利用Java核心類庫,擴(kuò)展類庫和CLASSPATH中的類庫中的Class。
還不死心!再嘗試一下這種情形,咱們把那個Class也放入到CLASSPATH中,讓systemclassloader能夠識別和載入。然后咱們通過自己的classloader來從指定的class文件中載入那個Class(不能夠委托parent載入,因?yàn)槿绱藭籹ystemclassloader從CLASSPATH中將其載入),然后實(shí)例化一個Object,并造型成那個Class,如此JVM也識別那個Class(因?yàn)閟ystemclassloader能夠定位和載入那個Class從CLASSPATH中),載入的也不是CLASSPATH中的那個Class,而是從CLASSPATH外動態(tài)載入的,如此總行了吧!十分不幸的是,這時會顯現(xiàn)“java.lang.ClassCastException”違例。
什么緣故呢?咱們也來分析一下,不錯,咱們盡管從CLASSPATH外利用咱們自己的classloader動態(tài)載入了那個Class,但將它的實(shí)例造型的時候是JVM會利用systemclassloader來再次載入那個Class,并嘗試將利用咱們的自己的classloader載入的Class的一個實(shí)例造型為systemclassloader載入的那個Class(另外的一個)。大伙兒發(fā)覺什么問題了嗎?也確實(shí)是咱們嘗試將從一個classloader載入的Class的一個實(shí)例造型為另外一個classloader載入的Class,盡管這兩個Class的名字一樣,乃至是從同一個class文件中載入。但不幸的是JVM卻以為那個兩個Class是不同的,即JVM以為不同的classloader載入的相同的名字的Class(即便是從同一個class文件中載入的)是不同的!如此做的緣故我想可能也是要緊出于平安性考慮,如此就保證所有的核心Java類都是systemclassloader載入的,咱們無法用自己的classloader載入的相同名字的Class的實(shí)例來替換它們的實(shí)例。
看到那個地址,伶俐的讀者必然想到了該如何動態(tài)載入咱們的Class,實(shí)例化,造型并挪用了吧!
那確實(shí)是利用面向?qū)ο蟮拇篌w特性之一的多形性。咱們把咱們動態(tài)載入的Class的實(shí)例造型成它的一個systemclassloader所能識別的父類就好了!這是什么緣故呢?咱們?nèi)允且賮矸治鲆淮巍.?dāng)咱們用咱們自己的classloader來動態(tài)載入這咱們只要把那個Class的時候,發(fā)覺它有一個父類Class,在載入它之前JVM先會載入那個父類Class,那個父類Class是systemclassloader所能識別的,依照委托機(jī)制,它將由systemclassloader載入,然后咱們的classloader再載入那個Class,創(chuàng)建一個實(shí)例,造型為那個父類Class,注意了,造型成那個父類Class的時候(也確實(shí)是上溯)是面向?qū)ο蟮膉ava語言所許諾的而且JVM也支持的,JVM就利用systemclassloader再次載入那個父類Class,然后將此實(shí)例造型為那個父類Class。大伙兒能夠從那個進(jìn)程發(fā)覺那個父類Class都是由systemclassloader載入的,也確實(shí)是同一個classloader載入的同一個Class,因此造型的時候可不能顯現(xiàn)任何異樣。而依照多形性,挪用那個父類的方式時,真正執(zhí)行的是那個Class(非父類Class)的覆蓋了父類方式的方式。這些方式中也能夠引用systemclassloader不能識別的Class,因?yàn)橐勒胀ūP負(fù)責(zé)原那么,只要載入那個Class的classloader即咱們自己概念的classloader能夠定位和載入這些Class就好了。
如此咱們就能夠夠事前概念好一組接口或基類并放入CLASSPATH中,然后在執(zhí)行的時
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 未來五年新形勢下工礦建筑磚石工程行業(yè)順勢崛起戰(zhàn)略制定與實(shí)施分析研究報告
- 2025年光伏支架全球市場競爭格局與發(fā)展趨勢報告
- 雨污水管網(wǎng)及外網(wǎng)工程施工設(shè)計(jì)方案
- 醫(yī)院門診分診體系建設(shè)方案
- 森林晚會活動方案策劃(3篇)
- 暖坑施工方案(3篇)
- 標(biāo)準(zhǔn)結(jié)構(gòu)施工方案(3篇)
- led應(yīng)急預(yù)案演習(xí)(3篇)
- 施工方案現(xiàn)場講解(3篇)
- 大足鞋店活動策劃方案(3篇)
- 企業(yè)薪資和經(jīng)濟(jì)效益掛鉤考核辦法
- 員工隱私安全意識培訓(xùn)課件
- 預(yù)防接種規(guī)范知識培訓(xùn)課件
- 部隊(duì)裝備換季保養(yǎng)課件
- DB 5303∕T 23-2024 《露地甜櫻桃種植技術(shù)規(guī)程》
- 《微壓富氧康養(yǎng)整體空間設(shè)備》
- 衛(wèi)星互聯(lián)網(wǎng)基礎(chǔ)知識培訓(xùn)課件
- 2025年敖漢旗就業(yè)服務(wù)中心招聘第一批公益性崗位人員的112人模擬試卷含答案詳解
- 婚姻家庭繼承實(shí)務(wù)講座
- 新內(nèi)瘺穿刺護(hù)理
- 鉗工個人實(shí)習(xí)總結(jié)
評論
0/150
提交評論