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

源碼解析Java類加載器

 更新時(shí)間:2020年12月06日 09:05:13   作者:小小小小小樂  
這篇文章主要給大家介紹了Java類加載器源碼解析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

參考內(nèi)容:

我們都知道Java的類加載器結(jié)構(gòu)為下圖所示(JDK8及之前,JDK9進(jìn)行了模塊化):

關(guān)于三層類加載器、雙親委派機(jī)制,本文不再板書,讀者可自行百度。

那么在JDK的源碼中,三層結(jié)構(gòu)的具體實(shí)現(xiàn)是怎么樣的呢?

Bootstrap ClassLoader(引導(dǎo)類加載器)

引導(dǎo)類加載器是由C++實(shí)現(xiàn)的,并非Java代碼實(shí)現(xiàn),所以在Java代碼中是無法獲取到該類加載器的。

一般大家都稱類加載器分為四種(引導(dǎo)類、擴(kuò)展類、系統(tǒng)類以及用戶自定義的類加載器),但其實(shí)在JVM虛擬機(jī)規(guī)范中的支持兩種類型的類加載器,分別為引導(dǎo)類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader),所以擴(kuò)展類和系統(tǒng)類也可以統(tǒng)稱為自定義類加載器。

Extension ClassLoader(擴(kuò)展類加載器)和Appclass Loader(系統(tǒng)類加載器)

擴(kuò)展類加載器和系統(tǒng)類加載器都是由Java語言編寫,具體實(shí)現(xiàn)為sum.misc.Launcher中的兩個(gè)內(nèi)部類ExtClassLoader和AppClassLoader實(shí)現(xiàn),我們進(jìn)入到LaunchLacher這個(gè)類中看看(這個(gè)類在oracle jdk是沒有公開源碼的,需要看具體源碼的讀者可以下載open jdk中查看具體源碼,筆者這里就只是使用IDEA反編譯后生成的代碼進(jìn)行解析):

首先是Laucncher的構(gòu)造方法:

public Launcher() {
  Launcher.ExtClassLoader var1;
  try {
  	// 獲取擴(kuò)展類加載器
   var1 = Launcher.ExtClassLoader.getExtClassLoader(); 
  } catch (IOException var10) {
   throw new InternalError("Could not create extension class loader", var10);
  }

  try {
  	// 獲取系統(tǒng)類加載器
   this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); 
  } catch (IOException var9) {
   throw new InternalError("Could not create application class loader", var9);
  }
		// 此處是將系統(tǒng)類加載器設(shè)置為當(dāng)前線程的上下文加載器
  Thread.currentThread().setContextClassLoader(this.loader); 
  String var2 = System.getProperty("java.security.manager");
  if (var2 != null) {
   SecurityManager var3 = null;
   if (!"".equals(var2) && !"default".equals(var2)) {
    try {
     var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
    } catch (IllegalAccessException var5) {
    } catch (InstantiationException var6) {
    } catch (ClassNotFoundException var7) {
    } catch (ClassCastException var8) {
    }
   } else {
    var3 = new SecurityManager();
   }

   if (var3 == null) {
    throw new InternalError("Could not create SecurityManager: " + var2);
   }

   System.setSecurityManager(var3);
  }

 }

可以看到在Launcher的構(gòu)造方法中定義了一個(gè)Launcher.ExtClassLoader類型的局部變量var1(這里是反編譯后的變量名),并調(diào)用Launcher.ExtClassLoader.getExtClassLoader()方法給該局部變量賦值,以及調(diào)用Launcher.AppClassLoader.getAppClassLoader(var1);給實(shí)例變量(類型為Launcher.AppClassLoader)賦值,需要注意的是,在給系統(tǒng)類加載器賦值時(shí),將擴(kuò)展類加載器作為參數(shù)傳入到了方法中。

同時(shí),在構(gòu)造方法中,將系統(tǒng)類加載器設(shè)置為了當(dāng)前線程的上下文類加載器,關(guān)于上下文類加載器,主要用于基礎(chǔ)類型調(diào)用回用戶代碼時(shí)方法父類加載器區(qū)請求子類加載器完成類加載的行為,主要用于JDBC、JNDI等SPI服務(wù)提供者接口,這里不詳細(xì)展開。

上述源碼中的**getExtClassLoader()與getAppClassLoader()**方法源碼如下:

getExtClassLoader()是Launcher中的內(nèi)部類ExtClassLoader(擴(kuò)展類加載器)的一個(gè)靜態(tài)方法:

// 這是ExtClassLoader類內(nèi)部的定義
private static volatile Launcher.ExtClassLoader instance;// 單例模式實(shí)例對象

public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
// 從這里可以看出,ExtClassLoader是一個(gè)由double-checking形成的懶漢式單例對象
   if (instance == null) { 
    Class var0 = Launcher.ExtClassLoader.class;
    synchronized(Launcher.ExtClassLoader.class) {
     if (instance == null) {
      instance = createExtClassLoader(); // 創(chuàng)建ExtClassLoader
     }
    }
   }

   return instance;
  }

// createExtClassLoader()方法
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
   try {
    return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
     public Launcher.ExtClassLoader run() throws IOException {
      File[] var1 = Launcher.ExtClassLoader.getExtDirs();
      int var2 = var1.length;

      for(int var3 = 0; var3 < var2; ++var3) {
       MetaIndex.registerDirectory(var1[var3]);
      }

      return new Launcher.ExtClassLoader(var1); // 調(diào)用構(gòu)造方法
     }
    });
   } catch (PrivilegedActionException var1) {
    throw (IOException)var1.getException();
   }
  }

// ExtClassLoader的構(gòu)造方法
public ExtClassLoader(File[] var1) throws IOException {
			// 此處第二個(gè)參數(shù)需要格外注意!!,我們進(jìn)入父類的構(gòu)造方法查看該參數(shù)是什么
   super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); 
   SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
  }

// 父類URLClassLoader的構(gòu)造方法
// 此處的第二個(gè)參數(shù)是父類構(gòu)造器的引用,也就解釋了為什么在調(diào)用獲得ExtClassLoader的
public URLClassLoader(URL[] urls, ClassLoader parent, getParent()方法獲取父類構(gòu)造器為null
       URLStreamHandlerFactory factory) {
  super(parent);
  // this is to make the stack depth consistent with 1.1
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
   security.checkCreateClassLoader();
  }
  acc = AccessController.getContext();
  ucp = new URLClassPath(urls, factory, acc);
 }

getAppClassLoader()是Launcher中的內(nèi)部類AppClassLoader(系統(tǒng)類加載器)的一個(gè)靜態(tài)方法:

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
   final String var1 = System.getProperty("java.class.path");
   final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
   return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
    public Launcher.AppClassLoader run() {
     URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
     return new Launcher.AppClassLoader(var1x, var0); // 與擴(kuò)展類加載器不同的是,系統(tǒng)類加載器并不是單例模式的
    }
   });
  }
// AppClassLoader的構(gòu)造方法
AppClassLoader(URL[] var1, ClassLoader var2) { 
 // 這里的var2 對應(yīng)上述getAppClassLoader()方法中的var0,而var0對應(yīng)的就是Launcher的構(gòu)造方法中獲取到的ExtClassLoader
 // 在ExtClassLoader源碼的分析中,我們知道這個(gè)var2代表的就是父類構(gòu)造器,所以此處就是將AppClassLoader的父類設(shè)置為ExtClassLoader
   super(var1, var2, Launcher.factory);
   this.ucp.initLookupCache(this);
  }

通過上述兩個(gè)方法,就可以解釋為什么在獲取擴(kuò)展類加載器的父類時(shí)為null(即引導(dǎo)加載器),以及不同類加載器看似是繼承(Inheritance)關(guān)系,實(shí)際上是包含關(guān)系。在下層加載器中,包含著上層加載器的引用。

ClassLoader抽象類

上述的ExtClassLoader和AppClassLoader均繼承于ClassLoader類,ClassLoader抽象類也是類加載機(jī)制的基石,接下來我們就進(jìn)入到該類中,看看它的一些主要方法。

public final classLoader getParent()

返回該類加載器的超類加載器

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

加載名稱為name的類,返回結(jié)果為java.lang.Class類的實(shí)例。如果找不到類,則返ClassNotFoundException異常。該方法中的邏輯就是雙親委派模式的實(shí)現(xiàn)。

protected class<?> findClass(string name)throws ClassNotFoundException

  • 查找二進(jìn)制名稱為name的類,返回結(jié)果為java.lang.Class類的實(shí)例。這是一個(gè)受保護(hù)的方法,JVM鼓勵(lì)我們重寫此方法,需要自定義加載器遵循雙親委托機(jī)制,該方法會(huì)在檢查完父類加載器之后被loadClass()方法調(diào)用。
  • 在JDK1.2之前,在自定義類加載時(shí),總會(huì)去繼承ClassLoader類并重寫loadClass方法,從而實(shí)現(xiàn)自定義的類加載類。但是在JDK1.2之后已不再建議用戶去覆蓋loadClass()方法,而是建議把自定義的類加載邏輯寫在findClass()方法中,從前面的分析可知, findClass()方法是在loadClass()方法中被調(diào)用的,當(dāng)loadclass()方法中父加載器加載失敗后,則會(huì)調(diào)用自己的findClass()方法來完成類加載,這樣就可以保證自定義的類加載器也符合雙親委托模式。
  • 需要注意的是ClassLoader類中并沒有實(shí)現(xiàn)findClass()方法的具體代碼邏輯,取而代之的是拋出ClassNotFoundException異常,同時(shí)應(yīng)該知道的是findClass方法通常是和defineClass方法一起使用的。一般情況下,在自定義類加載器時(shí),會(huì)直接覆蓋ClassLoader的findClass()方法并編寫加載規(guī)則,取得要加載類的字節(jié)碼后轉(zhuǎn)換成流,然后調(diào)用defineClass()方法生成類的Class對象。

protected final Class<?> defineClass(String name, byte[] b, int off,int len)

  • 根據(jù)給定的字節(jié)數(shù)組b轉(zhuǎn)換為Class的實(shí)例,off和len參數(shù)表示實(shí)際Class信息在byte數(shù)組中的位置和長度,其中byte數(shù)組b是ClassLoader從外部獲取的。這是受保護(hù)的方法,只有在自定義ClassLoader子類中可以使用。
  • defineClass()方法是用來將byte字節(jié)流解析郕VM能夠識別的cClass對象(ClassLoader中已實(shí)現(xiàn)該方法邏輯),通過這個(gè)方法不僅能夠通過class文件實(shí)例化class對象,也可以通過其他方式實(shí)例化class對象,如通過網(wǎng)絡(luò)接收一個(gè)類的字節(jié)碼,然后轉(zhuǎn)換為byte字節(jié)流創(chuàng)建對應(yīng)的Class對象。
  • defineClass()方法通常與findClass()方法一起使用,一般情況下,在自定義類加載器時(shí),會(huì)直接覆蓋ClassLoader的findClass()方法并編寫加載規(guī)則,取得要加載炎的字節(jié)碼后轉(zhuǎn)換成流,然后調(diào)用defineClass()方法生成類的Class對象。

protected final void resoiveClass(class<?> c)

  • 鏈接指定的一個(gè)Java類。使用該方法可以使用類的Class對象創(chuàng)建完成的同時(shí)也被解析。前面我們說鏈接階段主要是對字節(jié)碼進(jìn)行驗(yàn)證,為類變量分配內(nèi)存并設(shè)置初始值同時(shí)將字節(jié)碼文件中的符號引用轉(zhuǎn)換為直接引用。

protected final Class<?> findLoadedClass(String name)

  • 查找名稱為name的已經(jīng)被加載過的類,返回結(jié)果為java.lang.Class類的實(shí)例。這個(gè)方法是final方法,無法被修改。

private final ClassLoader parent;

  • 它也是一個(gè)ClassLoader的實(shí)例,這個(gè)字段所表示的ClassLoader也稱為這個(gè)ClassLoader的雙親。在類加載的過程中,classLoader可能會(huì)將某些請求交予自己的雙親處理。

關(guān)于這些方法,不一一展開,主要看一下loadClass()和findClass()。

loadClass()

public Class<?> loadClass(String name) throws ClassNotFoundException {
		// loadClass調(diào)用重載含有兩個(gè)參數(shù)的loadClass,其中第二個(gè)參數(shù)表示在加載時(shí)是否解析,默認(rèn)為false
  return loadClass(name, false); 
 }
// 含有兩個(gè)參數(shù)的重載loadClass方法
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{// resolve:true->加載class的同時(shí)進(jìn)行解析操作
  synchronized (getClassLoadingLock(name)) {// 同步操作,保證只能加載一次
   // 首先在緩存中判斷是否已經(jīng)加載同名的類
   Class<?> c = findLoadedClass(name);
   if (c == null) {
    long t0 = System.nanoTime();
    // 此處就是雙親委派機(jī)制的具體實(shí)現(xiàn),其實(shí)就是讓父類加載器先去加載。
    try {
     // 獲取當(dāng)前類加載器的父類加載器
     if (parent != null) {
      // 如果存在父類加載器,則調(diào)用父類加載器的loadClass進(jìn)行加載(雙親委派)
      c = parent.loadClass(name, false);
     } else { 
     		 // parent == null:父類加載器是引導(dǎo)類加載器
      c = findBootstrapClassOrNull(name);
     }
    } catch (ClassNotFoundException e) {
     // ClassNotFoundException thrown if class not found
     // from the non-null parent class loader
    }
				// 當(dāng)前類加載器的父類加載器未加載此類 or 當(dāng)前類加載器未加載此類
    if (c == null) { 
     // If still not found, then invoke findClass in order
     // to find the class.
     long t1 = System.nanoTime();
     // 調(diào)用當(dāng)前類加載器的findClass()
     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) {// 是否進(jìn)行解析操作
    resolveClass(c);
   }
   return c;
  }
 }

findClass()

//在ClassLoader中的findClass()方法
rotected Class<?> findClass(String name) throws ClassNotFoundException {
  throw new ClassNotFoundException(name);
 }

可以看到,在ClassLoader中的findCLass()方法直接拋出異常,所以具體的實(shí)現(xiàn)是由子類進(jìn)行重寫實(shí)現(xiàn)了;在ClassLoader的子類SecureClassLoader的子類URLClassLoader中對該方法進(jìn)行了重寫。

URLClassLoader中的findCLass()方法

protected Class<?> findClass(final String name)
  throws ClassNotFoundException
 {
  final Class<?> result;
  try {
   result = AccessController.doPrivileged(
    new PrivilegedExceptionAction<Class<?>>() {
     public Class<?> run() throws ClassNotFoundException {
      String path = name.replace('.', '/').concat(".class");// 類名路徑字符串格式替換
      Resource res = ucp.getResource(path, false);// 獲得class源文件
      if (res != null) {
       try {
       	// 調(diào)用defineClass()方法獲得要加載的類對應(yīng)的Class對象,
       	// defineClass()的作用就是根據(jù)給定的class源文件返回一個(gè)對應(yīng)的Class對象
        return defineClass(name, res);
       } catch (IOException e) {
        throw new ClassNotFoundException(name, e);
       }
      } else {
       return null;
      }
     }
    }, acc);
  } catch (java.security.PrivilegedActionException pae) {
   throw (ClassNotFoundException) pae.getException();
  }
  if (result == null) {
   throw new ClassNotFoundException(name);
  }
  return result;
 }

最后補(bǔ)充一點(diǎn)關(guān)于數(shù)組類加載的細(xì)節(jié)

數(shù)組類的Class對象,不是由類加載器去創(chuàng)建的,而是在Java運(yùn)行期JVM根據(jù)需要自動(dòng)創(chuàng)建的。對于數(shù)組類的類加載器來說,是通過Class.getClassLoader()返回的,與數(shù)組當(dāng)中元素類型的類加載器是一樣的,如果數(shù)組當(dāng)中的元素類型是基本數(shù)據(jù)類型,數(shù)組類是沒有類加載器的。

到此這篇關(guān)于Java類加載器的文章就介紹到這了,更多相關(guān)Java類加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA中編寫并運(yùn)行shell腳本的實(shí)現(xiàn)

    IDEA中編寫并運(yùn)行shell腳本的實(shí)現(xiàn)

    這篇文章主要介紹了IDEA中編寫并運(yùn)行shell腳本的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Spring Boot與前端配合與Idea配置部署操作過程

    Spring Boot與前端配合與Idea配置部署操作過程

    這篇文章主要介紹了Spring Boot與前端配合與Idea配置部署的操作過程,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2018-02-02
  • Java設(shè)計(jì)模式之java策略模式詳解

    Java設(shè)計(jì)模式之java策略模式詳解

    這篇文章主要介紹了Java經(jīng)典設(shè)計(jì)模式之策略模式,簡單說明了策略模式的概念、原理并結(jié)合實(shí)例形式分析了java策略模式的具有用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2021-09-09
  • 微信游戲打飛機(jī)游戲制作(java模擬微信打飛機(jī)游戲)

    微信游戲打飛機(jī)游戲制作(java模擬微信打飛機(jī)游戲)

    java模擬微信打飛機(jī)游戲,大家參考使用吧
    2013-12-12
  • SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制

    SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制

    這篇文章主要給大家介紹了關(guān)于SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-01-01
  • feign調(diào)用中文參數(shù)被encode編譯的問題

    feign調(diào)用中文參數(shù)被encode編譯的問題

    這篇文章主要介紹了feign調(diào)用中文參數(shù)被encode編譯的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳解SpringBoot中RestTemplate的幾種實(shí)現(xiàn)

    詳解SpringBoot中RestTemplate的幾種實(shí)現(xiàn)

    這篇文章主要介紹了詳解SpringBoot中RestTemplate的幾種實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • springboot如何統(tǒng)一設(shè)置時(shí)區(qū)

    springboot如何統(tǒng)一設(shè)置時(shí)區(qū)

    這篇文章主要介紹了springboot如何統(tǒng)一設(shè)置時(shí)區(qū)問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • java中多線程的超詳細(xì)介紹

    java中多線程的超詳細(xì)介紹

    這篇文章主要給大家介紹了關(guān)于java中多線程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java內(nèi)存分配多種情況的用法解析

    Java內(nèi)存分配多種情況的用法解析

    這篇文章主要介紹了Java內(nèi)存分配多種情況的用法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評論

怀宁县| 永年县| 宁国市| 来凤县| 壶关县| 桃园县| 山阴县| 邢台县| 泽库县| 石家庄市| 遂溪县| 尚义县| 桃园县| 灵寿县| 天水市| 巨鹿县| 平陆县| 丽江市| 荥阳市| 原平市| 长宁县| 泌阳县| 康平县| 遵化市| 水城县| 临朐县| 岫岩| 宁晋县| 千阳县| 明光市| 江孜县| 宝山区| 禄丰县| 昌乐县| 清镇市| 漾濞| 通许县| 仁怀市| 历史| 湄潭县| 新宾|