Java ClassLoader --第二篇【类加载源码解读】

本篇主要讲解 java自带的三个类加载器 是如何工作的,主要 关注 Launcher.class, URLClassLoader.class , ClassLoader.class 这三个类的源码,介绍 双亲委派模式 是如何实现以及如何工作的

 

 Launcher.class 该类是java的入口

  由于该类有C++ 编写,Idea 反编译的 所以源码阅读性不高,我们只需要关注几个核心的方法

public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            //初始化扩展类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
             // 初始化应用类加载器,并将 扩展类加载器作为参数
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        // 将应用类加载器设置为当前线程的加载器
        Thread.currentThread().setContextClassLoader(this.loader);
        。。。。
        }

    }

上面的代码入口 首先初始化 扩展类加载器 并且作为参数 初始化应用类加载器,【这里就实现了 应用类的加载器的父类为扩展类而非通过继承管线】,设置 应用类加载器为当前线程的类加载器。

 ExtClassLoader

static class ExtClassLoader extends URLClassLoader {
//继承了 URLClassLoader 
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
    //获取加载路径    
    final File[] var0 = getExtDirs();
   //构造 ExtClassLoader
    try {
      return (Launcher.ExtClassLoader)AccessController.doPrivileged(new 
                  PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        int var1 = var0.length;
                        for(int var2 = 0; var2 < var1; ++var2) {
                            MetaIndex.registerDirectory(var0[var2]);
                        }
                        return new Launcher.ExtClassLoader(var0);
                    }
                });
            } catch (PrivilegedActionException var2) {
                throw (IOException)var2.getException();
            }
   ...
}
private static File[] getExtDirs() {
   // 由此可见 加载的 lib/ext 目录下的文件
    String var0 = System.getProperty("java.ext.dirs");
   ...
  }
}

AppClassLoader

static class AppClassLoader extends URLClassLoader {
   //由此可见 记载的是 class path 路径
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
    //构造AppClassLoader
    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);
         }
   });
  //此处重写了父类的 loadClass
  public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    return super.loadClass(var1, var2);
   }
}

此处虽然重写了LoadClass方法 但是最终还是调用了 父类的loadClass

URLClassLoader, URLClassLoader 继承了 SecureClassLoader,最终还是继承了 ClassLoader,这里着重讲述 URLClassLoader 

//查找类路径并 define
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);
                        if (res != null) {
                            try {
                                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;
 }
//加载类
public final Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        // First check if we have permission to access the package. This
        // should go away once we've added support for exported packages.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
       // 调用了 父类的 类加载器
        return super.loadClass(name, resolve);
    }

以上2个方法 是URLClassLoader 里面最重要的 2个方法, 一个是遵循双亲委派 的loadClass  一个是 查找类路径 并define class,稍后会断点该方法详解

ClassLoader

protected 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();
                    //各自加载器具体实现的 查找class 并define
                    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;
        }
    }

从该类的 loadClass 可以 明确体现出 双亲委派机制,当类加载请求到来时,先从缓存中查找该类对象, 首先让父类加载器去 加载,加载不到再让启动类加载器 去加载,最后都为空 则使用findClass()方法去加载

接下来我们将采用断点的方式 查看一个类是如何被加载的

//类名自己可以随意定义,此处是我自己随便建的一个类。
Class<?> object2 =  ClassLoader.getSystemClassLoader().loadClass("WaitLoadClass");
System.out.println("启动类加载器加载的类:"+ object2.hashCode());

1.代码 mian方法 运行Debug,如下图 可以看到程序进入了 AppClassLoader 下的 loadClass

2.进入下一个断点 进入ClassLoader下的loadClass,如下图 可以看到当前的类加载器为AppClassLoader, 父加载器 为ExtClassLoader

 

3.进入下一个断点, 再次进入ClassLoader下的loadClass,如下图 可以看到当前的类加载器已经为ExtClassLoader 父加载器为null

4.进入下一个断点,代码进入ExtClassLoader的 findClass方法,如下图

 

5.进入下一个断点,代码再次进入ExtClassLoader的 findClass方法 此时的类加载器已经为应用类加载器

6.再次进入下一个断点,程序结束,打印

总结:通过上面的6步断点操作,不难发现 当我们加载一个 普通类(非lib或者lib/ext) 下的类,首先会进入ClassLoader  loadCLass()方法 通过parent 进行比较来找到最顶级的加载器,未加载到就通过findClass,

在所在加载器的目录下查找,查找不到 再交给下一级的加载器来find,感兴趣的可以分别调试下 lib 下的 java.lang.String 类 与lib/ext 下的  com.sun.nio.zipfs.ZipUtils 各自分别是如何运行 loadCLass与findClass 的

说明:由于启动加载的类过多,此处调试为了方便 断点都是加了if 条件,先左键点击设置断点再右键点击断点处 具体 如下图

通过上面的源码以及断点调试 基本还原了 java ClassLoader的双亲委派模式,下期我们将介绍自定义类加载器。

END 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H风雨Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值