黑馬程序員--05.類加載器--03【從JVM加載類的過程再看類加載器】【從Java源碼再看雙親委派模型】

類加載器--3

從JVM加載類的過程再看類加載器

從Java源碼再看雙親委派模型

----------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

1.    從JVM加載類的過程再看類加載器

(1). 類加載過程的加載階段要點回顧

[1]. 在類加載過程加載階段中,JVM的規範之一是“通過全類名獲取定義此類的二進制字節(byte)”。

[2]. 但是JVM並沒有對這條規範“從哪裏獲取此類的二進制流”並且“怎樣獲取此類的二進制流”做出明確的規定

[3]. 進一步說:JVM設計團隊將“通過全類名獲取定義此類的二進制字節(byte)這個動作放到JVM外部去實現,以便讓應用程序自己決定如何去獲取所需要的類

(2). 通過加載階段認識類加載器

再次理解類加載器:實現通過全類名獲取定義此類的二進制字節(byte)這個動作代碼的模塊稱爲類加載器

2.    從Java源碼再看雙親委派模型

1). ClassLoader抽象類中loadClass的源碼

(1). 供JVM直接調用的是public訪問權限的loadClass()方法

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

在這個方法中,又一次調用了訪問權限是protected的重載的loadClass方法。這個方法體現了類加載器的雙親委託模型

(2). 雙親委託機制的源碼體現

[1]. 訪問權限爲protected的loadClass方法的調用者

默認的由JVM調用的public的loadClass調用這個方法在protected的loadClass第二個參數boolean resolve的位置傳入的參數是false

[2]. loadClass源碼的流程

【說明】代碼中使用了含有緩存cache機制雙親委託機制。首先在緩存cache中查找是否存在這個Class對象,如果存在就直接返回。否則再去使用雙親委託機制去加載這個Class對象。

step1. 檢測Class否載被加載過( 即cache中是否有此Class )

{1}. 如果已經被加載過並找到,去step7直接返回這個被加載過的Class對象

{2}. 如果沒有,跳轉到step2

step2. 檢查這個類加載器的父classloader是否存在

{1}. 如果當前類加載器父類加載器仍然存在,將調用父類類加載器loadClass()方法指定的類進行加載

{2}. 如果當前類加載器父類加載器不存在(沒有parent,那parent一定是Bootstrap classloader ),跳轉到step4

step3. 請求父類加載器指定的類進行載入

{1}. 如果成功到step7

{2}. 不成功到step5

step4. 請求JVM從Bootstrap 類加載器相應的類進行加載

{1}. 如果成功,跳轉到step 7

{2}. 如果不成功,執行step5。

step5. 調用類加載器findClass( )方法對指定的類進行加載 (從與此classloader相關的類路徑中尋找)

{1}. 如果找到則執行step7

{2}. 否則執行step6

step6. 拋出ClassNotFoundException.

step7. 返回Class.

[3]. loadClass源碼


2). ExtClassLoader、AppClassLoader的真實繼承體系

(1). SecureClassLoader類

[1]. 所處位置java.security

[2]. 源碼聲明

public class SecureClassLoader extends ClassLoader {…}

[3]. 特點

{1}. 直接繼承抽象類java.lang.ClassLoader

{2}. 這個類對java.lang.ClassLoader中的defineClass方法提供了重載的方法,增加了如下兩個重載方法。【注意這兩個重載的方法是final的,也就是不能被重寫

{2}1. protected final Class<?> defineClass(String name, byte[] b,int off, int len, CodeSource cs)

{2}2. protected final Class<?> defineClass(String name, java.nio.ByteBufferb, CodeSource cs)

注意】這兩個重載的方法內部實現都是對java.lang.ClassLoader的defineClass( )方法的簡單封裝。

{3}. 增加對系統默認策略檢索的權限的支持【瞭解即可】

(2). URLClassLoader類

[1]. 所處位置java.net

[2]. 源碼聲明

public class URLClassLoader extendsSecureClassLoader implementsCloseable {
    //字節碼的檢索路徑
private final URLClassPath ucp;
//…
//重寫了java.lang.ClassLoader類的findClass方法
protected Class<?> findClass(final String name) throws ClassNotFoundException{//…}
    //…
}

[3]. URLClassLoader作用

{1}. 這個類可以從指定的搜索路徑加載字節碼文件或者資源文件

{2}. 指定的路徑可以是對JAR文件的引用,也可以是對普通路徑的引用

[4]. 特點

{1}. 直接繼承java.security.SecureClassLoader

{2}. 重載defineClass( )方法, private訪問屬性

{3}. 重寫了父類的findClass( )方法

[5]. URLClassLoader重寫的父類defineClass( )源碼

{1}. 作用

指定的資源獲取Class類對象

{2}. 源碼

private Class defineClass(String name, Resource res)throws IOException {
    long t0 = System.nanoTime();
    int i = name.lastIndexOf('.');
    URLurl = res.getCodeSourceURL();
    if (i != -1) {
       String pkgname = name.substring(0, i);
       // Check ifpackage already loaded.
       Manifest man = res.getManifest();
       if (getAndVerifyPackage(pkgname, man,url) == null) {
           try {
                if (man != null) {
                    definePackage(pkgname, man,url);
                } else {
                    definePackage(pkgname, null, null, null, null, null, null, null);
                }
           } catch (IllegalArgumentException iae) {
                // parallel-capable class loaders:re-verify in case of a
                // race condition
               if (getAndVerifyPackage(pkgname, man, url) == null) {
                    // Should never happen
                    throw new AssertionError("Cannot find package " +
                                            pkgname);
                }
           }
       }
    }
    // Now read the class bytes anddefine the class
   java.nio.ByteBuffer bb = res.getByteBuffer();
    if (bb != null) {
       // Use(direct) ByteBuffer:
       CodeSigner[] signers = res.getCodeSigners();
       CodeSource cs = newCodeSource(url, signers);
       sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
       return defineClass(name, bb, cs);
    } else {
       byte[] b = res.getBytes();
       // must readcertificates AFTER reading bytes.
        CodeSigner[] signers =res.getCodeSigners();
       CodeSource cs = newCodeSource(url, signers);
       sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
       return defineClass(name, b, 0, b.length,cs);
    }
}

[6]. URLClassLoader重寫的父類findClass( )源碼

{1}. 作用

根據指定的類名URL路徑尋找加載指定的Class。其中指定的URL路徑被URLClassLoader的成員變量ucp所記錄

{2}. 源碼

protected Class<?> findClass(final String name)
        throws ClassNotFoundException
{
    try {
       return AccessController.doPrivileged(
           new PrivilegedExceptionAction<Class>() {
                public Class run() throws ClassNotFoundException {
                    String path = name.replace('.', '/').concat(".class");
                    Resource res =ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                       }
                    } else {
                        throw new ClassNotFoundException(name);
                    }
                }
           }, acc);
    } catch (java.security.PrivilegedActionExceptionpae) {
       throw (ClassNotFoundException)pae.getException();
    }
}

【結論】可以看到,findClass方法就是通過拼接路徑字符串獲取最終的class文件所在的路徑,並進行加載 String path = name.replace('.', '/').concat(".class");

(3). Launcher$AppClassLoader類

[1]. 位置:sun.misc.Launcher的內部

[2]. AppClassLoader源碼 (從Launcher內部摘取)

由於AppClassLoader是靜態內部類,依附於Launcher這個外部類,以下是源碼。

static class AppClassLoader extendsURLClassLoader{
    public static ClassLoader getAppClassLoader(ClassLoader paramClassLoader)
      throws IOException{
     String str = System.getProperty("java.class.path");
     File[] arrayOfFile = str == null ? new File[0] : Launcher.access$200(str);
 
      return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction(str, arrayOfFile,paramClassLoader)
      {
       public Launcher.AppClassLoader run() {
         URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.access$300(this.val$path);
 
         return new Launcher.AppClassLoader(arrayOfURL, this.val$extcl);
       }
     });
    }
 
   AppClassLoader(URL[] paramArrayOfURL, ClassLoader paramClassLoader){
      super(paramClassLoader, Launcher.factory);
    }
 
    public Class loadClass(String paramString, boolean paramBoolean) throws ClassNotFoundException{
      int i = paramString.lastIndexOf('.');
      if (i != -1) {
       SecurityManager localSecurityManager = System.getSecurityManager();
       if (localSecurityManager != null) {
         localSecurityManager.checkPackageAccess(paramString.substring(0, i));
       }
      }
      return super.loadClass(paramString, paramBoolean);
    }
 
    protected PermissionCollection getPermissions(CodeSourceparamCodeSource){
     PermissionCollection localPermissionCollection = super.getPermissions(paramCodeSource);
     localPermissionCollection.add(new RuntimePermission("exitVM"));
      return localPermissionCollection;
    }
 
    private void appendToClassPathForInstrumentation(StringparamString){
      assert (Thread.holdsLock(this));
 
      super.addURL(Launcher.getFileURL(new File(paramString)));
    }
 
    private static AccessControlContext getContext(File[]paramArrayOfFile)throws MalformedURLException{
     PathPermissions localPathPermissions = new PathPermissions(paramArrayOfFile);
 
     ProtectionDomain localProtectionDomain = new ProtectionDomain(newCodeSource(localPathPermissions.getCodeBase(), (Certificate[])null), localPathPermissions);
 
     AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] { localProtectionDomain});
 
      return localAccessControlContext;
    }
 
    static
    {
     ClassLoader.registerAsParallelCapable();
    }
  }

[3]. Launcher$AppClassLoader類的特點

{1}, 直接父類URLClassLoader而不是ExtClassLoader

{2}. 重寫父類loadClass方法

public Class loadClass(String paramString, boolean paramBoolean) throws ClassNotFoundException{
  int i = paramString.lastIndexOf('.');
  if (i != -1) {
   SecurityManager localSecurityManager = System.getSecurityManager();
    if (localSecurityManager != null) {
     localSecurityManager.checkPackageAccess(paramString.substring(0, i));
    }
  }
  return super.loadClass(paramString, paramBoolean);
}

【注意】儘管是重寫父類的public的方法,但是僅僅做了一些類名上的檢查就再一次調用了父類的loadClass方法,沒有實質性的改變。調用了父類的loadClass就代表着在查找類的時候採用的是雙親委託機制進行查找

(4). Launcher$ExtClassLoader類

[1]. 位置:sun.misc.Launcher的內部

[2]. ExtClassLoader源碼 (從Launcher內部摘取)

  static class ExtClassLoader extendsURLClassLoader
  {
    public static ExtClassLoader getExtClassLoader() throws IOException{
     File[] arrayOfFile = getExtDirs();
      try{
       return(ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction(arrayOfFile)
       {
         public Launcher.ExtClassLoader run() throws IOException {
           int i = this.val$dirs.length;
           for (int j = 0; j < i; j++) {
             MetaIndex.registerDirectory(this.val$dirs[j]);
           }
           return new Launcher.ExtClassLoader(this.val$dirs);
         } } );
      }catch (PrivilegedActionExceptionlocalPrivilegedActionException) {
      }
      throw((IOException)localPrivilegedActionException.getException());
    }
 
    void addExtURL(URL paramURL)
    {
      super.addURL(paramURL);
    }
 
    public ExtClassLoader(File[] paramArrayOfFile)
      throws IOException
    {
      super(null, Launcher.factory);
    }
 
    private static File[] getExtDirs() {
     String str = System.getProperty("java.ext.dirs");
     File[] arrayOfFile;
      if (str != null) {
       StringTokenizer localStringTokenizer = new StringTokenizer(str, File.pathSeparator);
 
       int i =localStringTokenizer.countTokens();
       arrayOfFile = new File[i];
       for (int j = 0; j < i; j++)
         arrayOfFile[j] = newFile(localStringTokenizer.nextToken());
      }
      else {
       arrayOfFile = new File[0];
      }
      return arrayOfFile;
    }
 
    private static URL[] getExtURLs(File[] paramArrayOfFile) throws IOException {
     Vector localVector = new Vector();
      for (int i = 0; i < paramArrayOfFile.length; i++){
       String[] arrayOfString = paramArrayOfFile[i].list();
       if (arrayOfString != null) {
         for (int j = 0; j < arrayOfString.length; j++) {
           if (!arrayOfString[j].equals("meta-index")) {
             File localFile = newFile(paramArrayOfFile[i], arrayOfString[j]);
             localVector.add(Launcher.getFileURL(localFile));
           }
         }
       }
      }
     URL[] arrayOfURL = newURL[localVector.size()];
     localVector.copyInto(arrayOfURL);
      return arrayOfURL;
    }
 
    public String findLibrary(String paramString)
    {
     paramString = System.mapLibraryName(paramString);
     URL[] arrayOfURL = super.getURLs();
     Object localObject = null;
      for (int i = 0; i < arrayOfURL.length; i++)
      {
       File localFile1 = newFile(arrayOfURL[i].getPath()).getParentFile();
       if ((localFile1 != null) &&(!localFile1.equals(localObject)))
       {
         String str = VM.getSavedProperty("os.arch");
         if (str != null) {
           localFile2 = new File(new File(localFile1, str), paramString);
           if (localFile2.exists()) {
             return localFile2.getAbsolutePath();
           }
         }
 
         File localFile2 = newFile(localFile1, paramString);
         if (localFile2.exists()) {
           return localFile2.getAbsolutePath();
         }
       }
       localObject = localFile1;
      }
      return null;
    }
 
    private static AccessControlContext getContext(File[]paramArrayOfFile)
      throws IOException
    {
     PathPermissions localPathPermissions = new PathPermissions(paramArrayOfFile);
 
     ProtectionDomain localProtectionDomain = new ProtectionDomain(new CodeSource(localPathPermissions.getCodeBase(),(Certificate[])null), localPathPermissions);
 
     AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] { localProtectionDomain});
 
      return localAccessControlContext;
    }
 
    static
    {
     ClassLoader.registerAsParallelCapable();
    }
  }

[3].ExtClassLoader的特點

{1}. ExtClassLoaderAppClassLoader一樣,都是靜態內部類

{2}. ExtClassLoaderAppClassLoader一樣,直接父類都是URLClassLoader

由此我們可以得到如下的Java類加載器的真實繼承體系結構圖

3). Java中ClassLoader繼承體系

(1). ClassLoader及其子類的真實繼承體系


【注意】java.lang.ClassLoader是一個抽象類

(2). ExtClassLoader和AppClassLoader之間的關係

ExtClassLoaderAppClassLoader之間的所謂的“父子關係”

AppClassLoader是通過繼承ClassLoader基類內部聚合字段privatefinal ClassLoader parent; 來體現的ExtClassLoader是其“父類”。

【注意】由於Java僅僅支持單繼承,同時AppClassLoader已經繼承了URLClassLoader這個類,所以AppClassLoader就不能再使用extends繼承ExtClassLoader這個類

但是爲了表達AppClassLoaderExtClassLoader的關係,採用AppClassLoader內部字段parent指向ExtClassLoader的實例

(3). 雙親委託機制的細節

[1]. 當AppClassLoader被實例化時候,JVM調用AppClassLoaderloadClass方法相應的類進行加載

[2]. AppClassLoader重寫了父類的loadClass方法,但是在源代碼最後一行還是調用了super.loadClass( )。這證明了AppClassLoader使用的類加載的方式仍然是雙親委託模型

[3]. 當調用父類的loadClass方法的時候,如果父類一直找不到要求加載的類,就會返回給子類進行加載。子類這個時候採用的是findClass方法進行類加載

注意ClassLoader的默認findClass方法僅僅拋出異常,沒有去查找這個類!!

注意】由於AppClassLoader和ExtClassLoader繼承的是URLClassLoader類,這個類卻重寫了ClassLoader這個類的findClass方法,所以調用AppClassLoader或者ExtClassLoader類的loadClass方法的時候執行到findClass的時候,真正被JVM調用findClassClassLoader的子類URLClassLoader重寫的findClass方法

----------- android培訓java培訓、java學習型技術博客、期待與您交流! ------------

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章