黑马程序员--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学习型技术博客、期待与您交流! ------------

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值