如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器-創(chuàng)新互聯(lián)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)建站成立十多年來,這條路我們正越走越好,積累了技術(shù)與客戶資源,形成了良好的口碑。為客戶提供成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、網(wǎng)站策劃、網(wǎng)頁設(shè)計(jì)、申請(qǐng)域名、網(wǎng)絡(luò)營銷、VI設(shè)計(jì)、網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。網(wǎng)站是否美觀、功能強(qiáng)大、用戶體驗(yàn)好、性價(jià)比高、打開快等等,這些對(duì)于網(wǎng)站建設(shè)都非常重要,創(chuàng)新互聯(lián)建站通過對(duì)建站技術(shù)性的掌握、對(duì)創(chuàng)意設(shè)計(jì)的研究為客戶提供一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進(jìn)步。

使用Class.forName()的方式加載我們需要的類。比如最常見的,當(dāng)我們進(jìn)行JDBC編程的時(shí)候,我們通過Class.forName()去加載JDBC的驅(qū)動(dòng)。

try {
 return Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
 // skip
}

那么為什么當(dāng)我們使用Class.forName()的方式去加載類的時(shí)候,如果類找不到,我們還要嘗試用Thread.currentThread.getContextLoader()獲取的類加載器去加載類呢?比如我們可能會(huì)碰到下面這種代碼:

try {
 return Class.forName(className);
} catch (ClassNotFoundException e) {
 // skip
}

ClassLoader ctxClassLoader = Thread.currentThread().getContextClassLoader();
if (ctxClassLoader != null) {
 try {
 clazz = ctxClassLoader.loadClass(className);
 } catch (ClassNotFoundException e) {
   // skip
 }
}

這里加粗的部分,就是使用了Thread.currentThread.getContextLoader()獲取的加載器去加載類。顯然,Class.forName()加載類的時(shí)候使用的類加載器可能和Thread.currentThread.getContextLoader()獲取的類加載器是不同的。那么為什么會(huì)出現(xiàn)不同呢?

JAVA的類加載器

要理解為什么會(huì)用到Thread.currentThread.getContextLoader()獲取的這個(gè)類加載器之前,我們先來了解下JVM里使用的類加載器(ClassLoader)。

JVM默認(rèn)有三種類加載器:

  1. Bootstrap Class Loader

  2. Extension Class Loader

  3. System Class Loader

Bootstrap Class Loader

Bootstrap Class Loader類加載器是JDK自帶的一款類加載器,用于加載JDK內(nèi)部的類。Bootstrap類加載器用于加載JDK中$JAVA_HOME/jre/lib下面的那些類,比如rt.jar包里面的類。Bootstrap類加載器是JVM的一部分,一般采用native代碼編寫。

Extension Class Loader

  Extension Class Loader類加載器主要用于加載JDK擴(kuò)展包里的類。一般$JAVA_HOME/lib/ext下面的包都是通過這個(gè)類加載器加載的,這個(gè)包下面的類基本上是以javax開頭的。

System Class Loader

System Class Loader類加載器也叫應(yīng)用程序類加載器(AppClassLoader)。顧名思義,這個(gè)類加載器就是用來加載開發(fā)人員自己平時(shí)寫的應(yīng)用代碼的類的。System類加載器是用于加載存放在classpath路徑下的那些應(yīng)用程序級(jí)別的類的。

下面的代碼列舉出了這三個(gè)類加載器:

public class MainClass {
 public static void main(String[] args) {
  System.out.println(Integer.class.getClassLoader());
  System.out.println(Logging.class.getClassLoader());
  System.out.println(MainClass.class.getClassLoader());
 }
}

其中獲取Bootstrap類加載器永遠(yuǎn)返回null值

null # Bootstrap類加載器
sun.misc.Launcher$ExtClassLoader@5e2de80c # Extension類加載器
sun.misc.Launcher$AppClassLoader@18b4aac2 # System類加載器

雙親委派模型

上面介紹的三種類加載器,并不是孤立的,他們之間有一個(gè)層次關(guān)系:

如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器

三個(gè)類加載器之間通過這個(gè)層次關(guān)系協(xié)同工作,一起負(fù)責(zé)類的加載工作。上面的這種層次模型稱為類加載器的“雙親委派”模型。雙親委派模型要求,除了最頂層的Bootstrap類加載器之外,所有的類加載器都必須有一個(gè)parent加載器。當(dāng)類加載器加載類的時(shí)候,首先檢查緩存中是否有已經(jīng)被加載的類。如果沒有,則優(yōu)先委托它的父加載器去加載這個(gè)類,父加載器執(zhí)行和前面子加載器一樣的工作,直到請(qǐng)求達(dá)到頂層的Bootstrap類加載器。如果父加載器不能加載需要的類,那么這個(gè)時(shí)候才會(huì)讓子加載器自己去嘗試加載這個(gè)類。工作原理類似于下面這種方式:

  如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器

我們可以通過JDK里ClassLoader里面的代碼一窺雙親委派機(jī)制的實(shí)現(xiàn)方式,代碼實(shí)現(xiàn)在ClassLoader.loadClass()里面

rotected Class<?> loadClass(String name, boolean resolve)
 throws ClassNotFoundException

 synchronized (getClassLoadingLock(name)) {
  // First, check if the class has already been loaded
  Class<?> c = findLoadedClass(name);
  if (c == null) {
   long t0 = System.nanoTime();
   try {
    if (parent != null) {
     c = parent.loadClass(name, false);
    } else {
     c = findBootstrapClassOrNull(name);
    }
   } catch (ClassNotFoundException e) {
    // ClassNotFoundException thrown if class not found
    // from the non-null parent class loader
   }

   if (c == null) {
    // If still not found, then invoke findClass in order
    // to find the class.
    long t1 = System.nanoTime();
    c = findClass(name);

    // this is the defining class loader; record the stats
    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    sun.misc.PerfCounter.getFindClasses().increment();
   }
  }
  if (resolve) {
   resolveClass(c);
  }
  return c;
 }

采用雙親委派的方式組織類加載器,一個(gè)好處是為了安全。如果我們自己定義了一個(gè)String類,希望將這個(gè)String類替換掉默認(rèn)Java中的java.lang.String的實(shí)現(xiàn)。

我們將自己實(shí)現(xiàn)的String類的class文件放到classpath路徑下,當(dāng)我們使用類加載器去加載我們實(shí)現(xiàn)的String類的時(shí)候,首先,類加載器會(huì)將請(qǐng)求委托給父加載器,通過層層委派,最終由Bootstrap類加載器加載rt.jar包里的String類型,然后一路返回給我們。在這個(gè)過程中,我們的類加載器忽略掉了我們放在classpath中自定義的String類。

如果沒有采用雙親委派機(jī)制,那么System類加載器可以在classpath路徑中找到String的class文件并加載到程序中,導(dǎo)致JDK中的String實(shí)現(xiàn)被覆蓋。所以類加載器的這種工作方式,在一定程度上保證了Java程序可以安全穩(wěn)定的運(yùn)行。

線程上下文類加載器

上面講了那么多類加載器相關(guān)的內(nèi)容,可還是沒有講到今天的主題,線程上下文類加載器。

到這里,我們已經(jīng)知道Java提供了三種類加載器,并且按照嚴(yán)格的雙親委派機(jī)制協(xié)同工作。表面上,似乎很完美,但正是這種嚴(yán)格的雙親委派機(jī)制導(dǎo)致在加載類的時(shí)候,存在一些局限性。

當(dāng)我們更加基礎(chǔ)的框架需要用到應(yīng)用層面的類的時(shí)候,只有當(dāng)這個(gè)類是在我們當(dāng)前框架使用的類加載器可以加載的情況下我們才能用到這些類。換句話說,我們不能使用當(dāng)前類加載器的子加載器加載的類。這個(gè)限制就是雙親委派機(jī)制導(dǎo)致的,因?yàn)轭惣虞d請(qǐng)求的委派是單向的。

雖然這種情況不多,但是還是會(huì)有這種需求。比較典型的,JNDI服務(wù)。JNDI提供了查詢資源的接口,但是具體實(shí)現(xiàn)由不同的廠商實(shí)現(xiàn)。這個(gè)時(shí)候,JNDI的代碼是由JVM的Bootstrap類加載器加載,但是具體的實(shí)現(xiàn)是用戶提供的JDK之外的代碼,所以只能由System類加載器或者其他用戶自定義的類加載器去加載,在雙親委派的機(jī)制下,JNDI獲取不到JNDI的SPI的實(shí)現(xiàn)。

為了解決這個(gè)問題,引入了線程上下文類加載器。通過java.lang.Thread類的setContextClassLoader()設(shè)置當(dāng)前線程的上下文類加載器(如果沒有設(shè)置,默認(rèn)會(huì)從父線程中繼承,如果程序沒有設(shè)置過,則默認(rèn)是System類加載器)。有了線程上下文類加載器,應(yīng)用程序就可以通過java.lang.Thread.setContextClassLoader()將應(yīng)用程序使用的類加載器傳遞給使用更頂層類加載器的代碼。比如上面的JNDI服務(wù),就可以利用這種方式獲取到可以加載SPI實(shí)現(xiàn)的類加載器,獲取需要的SPI實(shí)現(xiàn)類。

如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器

Java的優(yōu)點(diǎn)是什么

1. 簡(jiǎn)單,只需理解基本的概念,就可以編寫適合于各種情況的應(yīng)用程序;2. 面向?qū)ο螅?. 分布性,Java是面向網(wǎng)絡(luò)的語言;4. 魯棒性,java提供自動(dòng)垃圾收集來進(jìn)行內(nèi)存管理,防止程序員在管理內(nèi)存時(shí)容易產(chǎn)生的錯(cuò)誤。;5. 安全性,用于網(wǎng)絡(luò)、分布環(huán)境下的Java必須防止病毒的入侵。6. 體系結(jié)構(gòu)中立,只要安裝了Java運(yùn)行時(shí)系統(tǒng),就可在任意處理器上運(yùn)行。7. 可移植性,Java可以方便地移植到網(wǎng)絡(luò)上的不同機(jī)器。8.解釋執(zhí)行,Java解釋器直接對(duì)Java字節(jié)碼進(jìn)行解釋執(zhí)行。

上述就是小編為大家分享的如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

文章標(biāo)題:如何在Java中實(shí)現(xiàn)一個(gè)ThreadContext類加載器-創(chuàng)新互聯(lián)
瀏覽地址:http://muchs.cn/article30/dpdhso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、搜索引擎優(yōu)化、關(guān)鍵詞優(yōu)化服務(wù)器托管、電子商務(wù)App開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)