最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入解析Java中的Classloader的運(yùn)行機(jī)制

 更新時(shí)間:2015年11月11日 08:54:28   作者:aminqiao  
這篇文章主要介紹了Java中的Classloader的運(yùn)行機(jī)制,包括從JVM方面講解類加載器的委托機(jī)制等,需要的朋友可以參考下

java有兩種類型的classload,一種是user-defined的,一種是jvm內(nèi)置的bootstrap class loader,所有user-defined的class loader都是java.lang.ClassLoader的子類.

而jvm內(nèi)置的class loader有3種,分別是 Bootstrap ClassLoader, Extension ClassLoader(即ExtClassLoader),System ClassLoader(即AppClassLoader).

而jvm加載時(shí)的雙親委派 就不說了,javaeye上有很多文章都有介紹..
可以分別看一下他們的構(gòu)造器,其中Bootstrap ClassLoader是用c寫的.
java.lang.ClassLoader

  protected ClassLoader(ClassLoader parent) {
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
   security.checkCreateClassLoader();
 }
//給父loader賦值.
 this.parent = parent;
 initialized = true;
  }
  protected ClassLoader() {
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
   security.checkCreateClassLoader();
 }
//這邊將會(huì)把AppClassLoader付給父loader
 this.parent = getSystemClassLoader();
 initialized = true;
  }


這個(gè)構(gòu)造器有帶參數(shù)的,和不帶構(gòu)造器的2個(gè)。帶參數(shù)的構(gòu)造器傳入的是這個(gè) class loader的父loader,而不帶參數(shù)的構(gòu)造器則會(huì)把getSystemClassLoader()所返回的class loader當(dāng)作他自己的父裝載器.下面我們來(lái)看getSystemClassLoader()的代碼

public static ClassLoader getSystemClassLoader() {
//所返回的class loader在這個(gè)方法里面被賦值
 initSystemClassLoader();
 if (scl == null) {
   return null;
 }
 SecurityManager sm = System.getSecurityManager();
 if (sm != null) {
   ClassLoader ccl = getCallerClassLoader();
   if (ccl != null && ccl != scl && !scl.isAncestor(ccl)) {
 sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
   }
 }
 return scl;
  }

  private static synchronized void initSystemClassLoader() {
 if (!sclSet) {
   if (scl != null)
 throw new IllegalStateException("recursive invocation");
      sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
   if (l != null) {
 Throwable oops = null;
//在這邊被賦值
 scl = l.getClassLoader();
     ...................................
.......................................
 }
   }
   sclSet = true;
 }
  }

這邊的父class loader就是scl,也就是l.getClassLoader()所得到的,getClassLoader(),接下來(lái)看Launcher的源碼:
private static Launcher launcher = new Launcher();

  public static Launcher getLauncher() {
 return launcher;
  }

  private ClassLoader loader;

  public Launcher() {
 // Create the extension class loader
 ClassLoader extcl;
 try {
//這里傳入構(gòu)造器的parent為空
   extcl = ExtClassLoader.getExtClassLoader();
 } catch (IOException e) {
   throw new InternalError(
 "Could not create extension class loader");
 }

 // Now create the class loader to use to launch the application
 try {
//這邊可以看到默認(rèn)的loader就是AppClassLoader,也就是說getSystemClassLoader返回的就是AppClassLoader
   loader = AppClassLoader.getAppClassLoader(extcl);
 } catch (IOException e) {
   throw new InternalError(
 "Could not create application class loader");
 }
//每一個(gè)當(dāng)前線程一個(gè)classload,以防止多線程中的classload引起的混亂(這個(gè)是我自己理解的,呵呵)
 // Also set the context class loader for the primordial thread.
 Thread.currentThread().setContextClassLoader(loader);

 ...................................
................................................
  }

  /*
   * Returns the class loader used to launch the main application.
   */
  public ClassLoader getClassLoader() {
 return loader;
  }

從中我們看到AppClassLoader的父loader是ExtClassLoader,而ExtClassLoader的父loader是什么呢?我們?cè)趤?lái)看ExtClassLoader的構(gòu)造器:

public ExtClassLoader(File[] dirs) throws IOException {
   super(getExtURLs(dirs), null, factory);
   this.dirs = dirs;
 }

他的父loader為空,而他的頂級(jí)父類是java.lang.ClassLoader而當(dāng)傳入的parent為null時(shí),我們使用 ExtClassLoader load一個(gè)類時(shí),系統(tǒng)會(huì)調(diào)用Bootstrap ClassLoader.

protected synchronized Class<?> loadClass(String name, boolean resolve)
 throws ClassNotFoundException
  {
 // First, check if the class has already been loaded
 Class c = findLoadedClass(name);
 if (c == null) {
   try {
 if (parent != null) {
//先調(diào)用父loader來(lái)load.
   c = parent.loadClass(name, false);
 } else {
//調(diào)用Bootstrap ClassLoader來(lái)load
   c = findBootstrapClass0(name);
 }
   } catch (ClassNotFoundException e) {
     // If still not found, then invoke findClass in order
     // to find the class.
     c = findClass(name);
   }
 }
 if (resolve) {
   resolveClass(c);
 }
 return c;
  }

而這里findBootstrapClass0也就是調(diào)用Bootstrap ClassLoader這個(gè)最核心的class loader來(lái)load class.

最終我們可以看到getSystemClassLoader() 返回的class loader就是AppClassLoader.

Java Classloader機(jī)制解析

JDK默認(rèn)ClassLoader
JDK 默認(rèn)提供了如下幾種ClassLoader

Bootstrp loader
Bootstrp加載器是用C++語(yǔ)言寫的,它是在Java虛擬機(jī)啟動(dòng)后初始化的,它主要負(fù)責(zé)加載%JAVA_HOME%/jre/lib,-Xbootclasspath參數(shù)指定的路徑以及%JAVA_HOME%/jre/classes中的類。

ExtClassLoader 
Bootstrp loader加載ExtClassLoader,并且將ExtClassLoader的父加載器設(shè)置為Bootstrp loader.ExtClassLoader是用Java寫的,具體來(lái)說就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加載%JAVA_HOME%/jre/lib/ext,此路徑下的所有classes目錄以及java.ext.dirs系統(tǒng)變量指定的路徑中類庫(kù)。

AppClassLoader
Bootstrp loader加載完ExtClassLoader后,就會(huì)加載AppClassLoader,并且將AppClassLoader的父加載器指定為 ExtClassLoader。AppClassLoader也是用Java寫成的,它的實(shí)現(xiàn)類是 sun.misc.Launcher$AppClassLoader,另外我們知道ClassLoader中有個(gè)getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要負(fù)責(zé)加載classpath所指定的位置的類或者是jar文檔,它也是Java程序默認(rèn)的類加載器。

綜上所述,它們之間的關(guān)系可以通過下圖形象的描述:

2015111185228919.png (447×234)

雙親委托模型
Java中ClassLoader的加載采用了雙親委托機(jī)制,采用雙親委托機(jī)制加載類的時(shí)候采用如下的幾個(gè)步驟:

當(dāng)前ClassLoader首先從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載則直接返回原來(lái)已經(jīng)加載的類。

每個(gè)類加載器都有自己的加載緩存,當(dāng)一個(gè)類被加載了以后就會(huì)放入緩存,等下次加載的時(shí)候就可以直接返回了。

當(dāng)前classLoader的緩存中沒有找到被加載的類的時(shí)候,委托父類加載器去加載,父類加載器采用同樣的策略,首先查看自己的緩存,然后委托父類的父類去加載,一直到bootstrp ClassLoader.

當(dāng)所有的父類加載器都沒有加載的時(shí)候,再由當(dāng)前的類加載器加載,并將其放入它自己的緩存中,以便下次有加載請(qǐng)求的時(shí)候直接返回。

說到這里大家可能會(huì)想,Java為什么要采用這樣的委托機(jī)制?理解這個(gè)問題,我們引入另外一個(gè)關(guān)于Classloader的概念“命名空間”, 它是指要確定某一個(gè)類,需要類的全限定名以及加載此類的ClassLoader來(lái)共同確定。也就是說即使兩個(gè)類的全限定名是相同的,但是因?yàn)椴煌?ClassLoader加載了此類,那么在JVM中它是不同的類。明白了命名空間以后,我們?cè)賮?lái)看看委托模型。采用了委托模型以后加大了不同的 ClassLoader的交互能力,比如上面說的,我們JDK本生提供的類庫(kù),比如hashmap,linkedlist等等,這些類由bootstrp 類加載器加載了以后,無(wú)論你程序中有多少個(gè)類加載器,那么這些類其實(shí)都是可以共享的,這樣就避免了不同的類加載器加載了同樣名字的不同類以后造成混亂。

如何自定義ClassLoader
Java除了上面所說的默認(rèn)提供的classloader以外,它還容許應(yīng)用程序可以自定義classloader,那么要想自定義classloader我們需要通過繼承java.lang.ClassLoader來(lái)實(shí)現(xiàn),接下來(lái)我們就來(lái)看看再自定義Classloader的時(shí)候,我們需要注意的幾個(gè)重要的方法:

1.loadClass 方法

loadClass method declare

public Class<?> loadClass(String name) throws ClassNotFoundException

上面是loadClass方法的原型聲明,上面所說的雙親委托機(jī)制的實(shí)現(xiàn)其實(shí)就實(shí)在此方法中實(shí)現(xiàn)的。下面我們就來(lái)看看此方法的代碼來(lái)看看它到底如何實(shí)現(xiàn)雙親委托的。

loadClass method implement

public Class<?> loadClass(String name) throws ClassNotFoundException
 { 
return loadClass(name, false);
}

從上面可以看出loadClass方法調(diào)用了loadcClass(name,false)方法,那么接下來(lái)我們?cè)賮?lái)看看另外一個(gè)loadClass方法的實(shí)現(xiàn)。

Class loadClass(String name, boolean resolve)

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException  
 { // First, check if the class has already been loaded Class c = findLoadedClass(name);
//檢查class是否已經(jīng)被加載過了 if (c == null)
 {   
 try {   
if (parent != null) {     
 c = parent.loadClass(name, false); //如果沒有被加載,且指定了父類加載器,則委托父加載器加載。  
 } else {    
 c = findBootstrapClass0(name);//如果沒有父類加載器,則委托bootstrap加載器加載   } 
   } catch (ClassNotFoundException e) {     
 // If still not found, then invoke findClass in order     
// to find the class.     
 c = findClass(name);//如果父類加載沒有加載到,則通過自己的findClass來(lái)加載。   } 
 } 
 if (resolve) 
{   
 resolveClass(c); 
 } 
return c;
}

上面的代碼,我加了注釋通過注釋可以清晰看出loadClass的雙親委托機(jī)制是如何工作的。 這里我們需要注意一點(diǎn)就是public Class<?> loadClass(String name) throws ClassNotFoundException沒有被標(biāo)記為final,也就意味著我們是可以override這個(gè)方法的,也就是說雙親委托機(jī)制是可以打破的。另外上面注意到有個(gè)findClass方法,接下來(lái)我們就來(lái)說說這個(gè)方法到底是搞末子的。

2.findClass

我們查看java.lang.ClassLoader的源代碼,我們發(fā)現(xiàn)findClass的實(shí)現(xiàn)如下:

protected Class<?> findClass(String name) throws ClassNotFoundException
 { 
throw new ClassNotFoundException(name);
}

我們可以看出此方法默認(rèn)的實(shí)現(xiàn)是直接拋出異常,其實(shí)這個(gè)方法就是留給我們應(yīng)用程序來(lái)override的。那么具體的實(shí)現(xiàn)就看你的實(shí)現(xiàn)邏輯了,你可以從磁盤讀取,也可以從網(wǎng)絡(luò)上獲取class文件的字節(jié)流,獲取class二進(jìn)制了以后就可以交給defineClass來(lái)實(shí)現(xiàn)進(jìn)一步的加載。defineClass我們?cè)傧旅嬖賮?lái)描述。 ok,通過上面的分析,我們可以得出如下結(jié)論:

我們?cè)趯懽约旱腃lassLoader的時(shí)候,如果想遵循雙親委托機(jī)制,則只需要override findClass.

3.defineClass

我們首先還是來(lái)看看defineClass的源碼:

defineClass

protected final Class<?> defineClass(String name, byte[] b, int off, int len) 
throws ClassFormatError
{   
 return defineClass(name, b, off, len, null);
}

從上面的代碼我們看出此方法被定義為了final,這也就意味著此方法不能被Override,其實(shí)這也是jvm留給我們的唯一的入口,通過這個(gè)唯 一的入口,jvm保證了類文件必須符合Java虛擬機(jī)規(guī)范規(guī)定的類的定義。此方法最后會(huì)調(diào)用native的方法來(lái)實(shí)現(xiàn)真正的類的加載工作。

Ok,通過上面的描述,我們來(lái)思考下面一個(gè)問題:
假如我們自己寫了一個(gè)java.lang.String的類,我們是否可以替換調(diào)JDK本身的類?

答案是否定的。我們不能實(shí)現(xiàn)。為什么呢?我看很多網(wǎng)上解釋是說雙親委托機(jī)制解決這個(gè)問題,其實(shí)不是非常的準(zhǔn)確。因?yàn)殡p親委托機(jī)制是可以打破的,你完全可以自己寫一個(gè)classLoader來(lái)加載自己寫的java.lang.String類,但是你會(huì)發(fā)現(xiàn)也不會(huì)加載成功,具體就是因?yàn)獒槍?duì)java.*開頭的類,jvm的實(shí)現(xiàn)中已經(jīng)保證了必須由bootstrp來(lái)加載。

不遵循“雙親委托機(jī)制”的場(chǎng)景
上面說了雙親委托機(jī)制主要是為了實(shí)現(xiàn)不同的ClassLoader之間加載的類的交互問題,被大家公用的類就交由父加載器去加載,但是Java中確實(shí)也存在父類加載器加載的類需要用到子加載器加載的類的情況。下面我們就來(lái)說說這種情況的發(fā)生。

Java中有一個(gè)SPI(Service Provider Interface)標(biāo)準(zhǔn),使用了SPI的庫(kù),比如JDBC,JNDI等,我們都知道JDBC需要第三方提供的驅(qū)動(dòng)才可以,而驅(qū)動(dòng)的jar包是放在我們應(yīng) 用程序本身的classpath的,而jdbc 本身的api是jdk提供的一部分,它已經(jīng)被bootstrp加載了,那第三方廠商提供的實(shí)現(xiàn)類怎么加載呢?這里面JAVA引入了線程上下文類加載的概 念,線程類加載器默認(rèn)會(huì)從父線程繼承,如果沒有指定的話,默認(rèn)就是系統(tǒng)類加載器(AppClassLoader),這樣的話當(dāng)加載第三方驅(qū)動(dòng)的時(shí)候,就可 以通過線程的上下文類加載器來(lái)加載。
另外為了實(shí)現(xiàn)更靈活的類加載器OSGI以及一些Java app server也打破了雙親委托機(jī)制。

相關(guān)文章

  • RocketMQ重試機(jī)制及消息冪代碼實(shí)例解析

    RocketMQ重試機(jī)制及消息冪代碼實(shí)例解析

    這篇文章主要介紹了RocketMQ重試機(jī)制及消息冪代碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Spring核心思想之淺談IoC容器與依賴倒置(DI)

    Spring核心思想之淺談IoC容器與依賴倒置(DI)

    文章介紹了Spring的IoC和DI機(jī)制,以及MyBatis的動(dòng)態(tài)代理,通過注解和反射,Spring能夠自動(dòng)管理對(duì)象的創(chuàng)建和依賴注入,而MyBatis則通過動(dòng)態(tài)代理實(shí)現(xiàn)了接口方法到數(shù)據(jù)庫(kù)操作的映射,文章詳細(xì)解釋了Spring和MyBatis的工作原理,并通過示例代碼展示了它們的結(jié)合使用方式
    2025-01-01
  • Java為啥能跨平臺(tái)(原因解析)JDK/JRE/JVM的關(guān)系說明

    Java為啥能跨平臺(tái)(原因解析)JDK/JRE/JVM的關(guān)系說明

    JVM是Java虛擬機(jī),是Java程序的運(yùn)行環(huán)境,Java的跨平臺(tái)特性是由于JVM的適配性設(shè)計(jì),JDK、JRE、JVM三者的關(guān)系是JDK> JRE > JVM,本文介紹Java為啥能跨平臺(tái),JDK/JRE/JVM的關(guān)系說明,感興趣的朋友一起看看吧
    2025-12-12
  • 詳解java中DelayQueue的使用

    詳解java中DelayQueue的使用

    這篇文章主要介紹了java中DelayQueue的使用,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-10-10
  • Java 虛擬機(jī)棧詳解分析

    Java 虛擬機(jī)棧詳解分析

    在線程創(chuàng)建時(shí),JVM會(huì)為每個(gè)線程創(chuàng)建一個(gè)單獨(dú)的??臻g。JVM的棧內(nèi)存不需要是連續(xù)的。JVM在棧上會(huì)進(jìn)行兩個(gè)操作:壓入和彈出棧幀。對(duì)于一個(gè)特定的線程來(lái)說,棧被稱為運(yùn)行時(shí)棧。這個(gè)線程調(diào)用的每個(gè)方法會(huì)被存儲(chǔ)在響應(yīng)的運(yùn)行時(shí)棧里,包括了參數(shù),局部變量,計(jì)算媒介和其他數(shù)據(jù)
    2021-11-11
  • Springboot循環(huán)依賴的原因及解決

    Springboot循環(huán)依賴的原因及解決

    本文主要介紹了Spring Boot中的循環(huán)依賴問題,包括循環(huán)依賴的定義、產(chǎn)生原因以及解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-05-05
  • ajax實(shí)時(shí)監(jiān)測(cè)與springboot的實(shí)例分析

    ajax實(shí)時(shí)監(jiān)測(cè)與springboot的實(shí)例分析

    本文將介紹如何使用 AJAX 技術(shù)結(jié)合 Spring Boot 構(gòu)建一個(gè)實(shí)時(shí)反饋用戶輸入的應(yīng)用,我們將創(chuàng)建一個(gè)簡(jiǎn)單的輸入框,當(dāng)用戶在輸入框中鍵入文本時(shí),應(yīng)用將異步地向后端發(fā)送請(qǐng)求,感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • Java連接MySQL數(shù)據(jù)庫(kù)并實(shí)現(xiàn)數(shù)據(jù)交互功能

    Java連接MySQL數(shù)據(jù)庫(kù)并實(shí)現(xiàn)數(shù)據(jù)交互功能

    在現(xiàn)代應(yīng)用中,數(shù)據(jù)庫(kù)是不可或缺的一部分,Java 作為一種廣泛使用的編程語(yǔ)言,提供了豐富的 API 來(lái)與各種數(shù)據(jù)庫(kù)進(jìn)行交互,本文將詳細(xì)介紹如何在 Java 中連接 MySQL 數(shù)據(jù)庫(kù),并實(shí)現(xiàn)基本的數(shù)據(jù)交互功能,需要的朋友可以參考下
    2024-10-10
  • Spring?Boot整合郵箱發(fā)送郵件實(shí)例

    Spring?Boot整合郵箱發(fā)送郵件實(shí)例

    大家好,本篇文章主要講的是Spring?Boot整合郵箱發(fā)送郵件實(shí)例,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • SpringBoot整合Mybatis-Plus+Druid實(shí)現(xiàn)多數(shù)據(jù)源配置功能

    SpringBoot整合Mybatis-Plus+Druid實(shí)現(xiàn)多數(shù)據(jù)源配置功能

    本文主要講解springboot?+mybatisplus?+?druid?實(shí)現(xiàn)多數(shù)據(jù)源配置功能以及一些必要的準(zhǔn)備及代碼說明,具有一定的參考價(jià)值,感興趣的小伙伴可以借鑒一下
    2023-06-06

最新評(píng)論

鹰潭市| 富裕县| 稷山县| 彭泽县| 探索| 许昌市| 曲沃县| 英山县| 朔州市| 博白县| 哈密市| 济阳县| 晋江市| 金阳县| 钟山县| 上思县| 麟游县| 凤山市| 昔阳县| 视频| 南通市| 滦平县| 泰顺县| 广德县| 汕尾市| 中山市| 新昌县| 岫岩| 商都县| 讷河市| 陇川县| 张掖市| 班戈县| 富民县| 凤阳县| 定襄县| 陆河县| 邮箱| 垦利县| 齐齐哈尔市| 济源市|