zoukankan      html  css  js  c++  java
  • 【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析

    前言

    学习类加载必然离开不了sun.misc.Launcher这个类和Class.forName()这个方法。

    分析ClassLoader.getSystemClassLoader()这个流程可以明白下面几个知识点:

    • sun.misc.Launcher的初始化

    • 初次接触线程上下文类加载器(Thread context class loader)

    • 三个参数的Class.forName(String name, boolean initialize, ClassLoader loader)方法

    • 怎样修改JVM默认的系统类加载器

    • Launcher类中存在两个很重要的内部类:AppClassLoader和ExtClassLoader

    • Launcher类主要负责AppClassLoader的初始化/ExtClassLoader的初始化/线程上下文类加载器的初始化

    • Class.forName()是JDK提供给我们用于加载一个Class文件的方法

    第一步:ClassLoader.getSystemClassLoader()

    当客户端希望获取系统类加载器的时候,需要第一次调用ClassLoader.getSystemClassLoader()静态方法,该方法第一步即会去尝试获取一个sun.misc.Launcher

    sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
    

    第二步:Launcher实例的初始化

    这里的实例初始化是不区分方法的,这是Launcher类的静态变量的初始化,通过之前学习类加载的知识可以知道:类初始化后,类的静态变量会初始化。所以下面的代码得到调用:

        private static Launcher launcher = new Launcher();
    

    下面我们看一下Launcher的无参构造器:

    public class Launcher {
        public Launcher() {
            Launcher.ExtClassLoader var1;
            try {
                /**
                 * 调用静态内部类的静态方法:获取ExtClassLoader(第一次调用时会初始化)
                 * 这里的getExtClassLoader()方法使用了DCL模式创建一个ExtClassLoader的单例。
                 * 初始化的时候,内部包含ExtClassLoader的加载路径:java.ext.dirs
                 */
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
            try {
                /**
                 * 调用静态内部类的静态方法:获取AppClassLoader(第一次调用时会初始化)
                 * 这里的getAppClassLoader()方法没有什么特殊的,就是包含AppClassLoader的加载路径:java.class.path
                 */
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }
            //设置当前线程上下文类加载器为AppClassLoader
            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 {
                        //SecurityManager默认是被AppClassLoader加载的
                        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);
            }
        }
    }
    

    第三步:ClassLoader.initSystemClassLoader()

    说回ClassLoader.getSystemClassLoader()方法,初始化完成Launcher实例之后,下面就是初始化ClassLoader类中的SystemClassLoader了。

        private static synchronized void initSystemClassLoader() {
            //如果systemClassLoader没有被设置
            if (!sclSet) {
                if (scl != null)
                    throw new IllegalStateException("recursive invocation");
                //获取Launcher
                sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
                if (l != null) {
                    Throwable oops = null;
                    //获取Launcher的AppClassLoader
                    scl = l.getClassLoader();
                    try {
                        //使用SystemClassLoaderAction类暴露修改SystemClassLoader的功能给User
                        scl = AccessController.doPrivileged(
                                new SystemClassLoaderAction(scl));
                    } catch (PrivilegedActionException pae) {
                        oops = pae.getCause();
                        if (oops instanceof InvocationTargetException) {
                            oops = oops.getCause();
                        }
                    }
                    if (oops != null) {
                        if (oops instanceof Error) {
                            throw (Error) oops;
                        } else {
                            // wrap the exception
                            throw new Error(oops);
                        }
                    }
                }
                sclSet = true;
            }
        }
    

    第四步:利用SystemClassLoaderAction修改AppClassLoader

    SystemClassLoaderAction类不是内部类,它是ClassLoader平级的类,但不是public的。

    class SystemClassLoaderAction
                implements PrivilegedExceptionAction<ClassLoader> {
            private ClassLoader parent;
    
            SystemClassLoaderAction(ClassLoader parent) {
                this.parent = parent;
            }
    
            public ClassLoader run() throws Exception {
                //获取系统属性java.system.class.loader
                String cls = System.getProperty("java.system.class.loader");
                if (cls == null) {
                    //系统属性java.system.class.loader为空,返回默认的AppClassLoader【这种情况为默认情况】
                    return parent;
                }
                /**
                 * 若系统属性java.system.class.loader(设为X)不为空,反射获取x的class(X为二进制名字)
                 * 然后把X对应的类加载器反射初始化,设置为系统类加载器。
                 * 并将X设置为线程上下文类加载器。
                 * 注意这里的Class.forName(String name, boolean initialize, ClassLoader loader)方法
                 */
                Constructor<?> ctor = Class.forName(cls, true, parent)
                        .getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
                ClassLoader sys = (ClassLoader) ctor.newInstance(
                        new Object[] { parent });
                Thread.currentThread().setContextClassLoader(sys);
                return sys;
            }
        }
    

    第五步:三个参数的Class.forName(String name, boolean initialize, ClassLoader loader)方法

    • 参数1:类的全名
    • 参数2:true表示初始化这个class,false表示不需初始化这个class
    • 参数3:loader表示希望用哪个类加载器来加载该类
    • 返回 :代表这个名字的类的Class对象
      Class.forName(String name, boolean initialize, ClassLoader loader)方法的部分JavaDoc文档:
         * @param name       fully qualified name of the desired class
         * @param initialize if {@code true} the class will be initialized.
         *                   See Section 12.4 of <em>The Java Language Specification</em>.
         * @param loader     class loader from which the class must be loaded
         * @return           class object representing the desired class
    

    sun.misc.Launcher中的内部类

    AppClassLoader

    可以看到Launcher类中存在一个静态内部类AppClassLoader,其中包含如下方法,所以这就是该问题的答案:为何应用类加载器是从java.class.path路径中加载类?

            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);
                    }
                });
            }
    

    ExtClassLoader

    Launcher类中还存在一个静态内部类ExtClassLoader,其中包含如下方法,在首次创建扩展类加载器的时候被调用,所以这就是该问题的答案:为何扩展类加载器是从java.ext.dirs路径中加载类?

            private static File[] getExtDirs() {
                String var0 = System.getProperty("java.ext.dirs");
                File[] var1;
                if (var0 != null) {
                    StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                    int var3 = var2.countTokens();
                    var1 = new File[var3];
    
                    for(int var4 = 0; var4 < var3; ++var4) {
                        var1[var4] = new File(var2.nextToken());
                    }
                } else {
                    var1 = new File[0];
                }
    
                return var1;
            }
    
  • 相关阅读:
    WinForm控件之【DateTimePicker】
    WinForm控件之【ComboBox】
    WinForm控件之【CheckedListBox】
    第五章学习小结
    第四章学习小结
    第三章学习小结
    第二章学习小结
    iOS
    iOS
    iOS
  • 原文地址:https://www.cnblogs.com/1626ace/p/13584774.html
Copyright © 2011-2022 走看看