美文网首页
ClassLoader类加载器(四):双亲委托机制

ClassLoader类加载器(四):双亲委托机制

作者: bug喵喵 | 来源:发表于2020-12-12 14:25 被阅读0次

    一. 双亲委派机制

    当java应用程序需要加载类的时候,调用的ClassLoader首先会委托自己的父ClassLoader去加载类,如果父ClassLoader可以加载类,那么由父ClassLoader加载类,如果父ClassLoader不能加载类,那么才由当前调用的ClassLoader去加载类

    • 从JDK1.2开始,类的加载便采用了双亲委托机制,这种方式可以更好的保证Java平台的安全,例如:加载String类,由根加载器进行加载
    • 除了根加载器,其他的类加载器,有且只有一个父加载器
    • 父ClassLoader和当前ClassLoader不是继承关系
    • 每一个Class都可以通过getClassLoader()方法,找到加载自己的ClassLoader

    1. ClassLoader委托链

    image

    1.1 Bootstrap ClassLoader(根加载器)

    • 没有父ClassLoader
    • 从sun.boot.class.path所指定的目录加载类库
      • JAVA_HOME/lib下的jar包
      • 通过JVM参数-Xbootclasspath指定的目录
    • 主要加载JVM的核心类,如java.lang.*
    • 根加载器依赖于JVM的底层实现,属于虚拟机实现的一部分
    • 获取Bootstrap ClassLoader返回null
    public class JVMTest {
        public static void main(String[] args) {
            //String.class的ClassLoader是Bootstrap ClassLoader
            System.out.println(String.class.getClassLoader());
        }
    }
    
    
    //运行结果:
        null
    
    

    1.2 Extension ClassLoader(扩展加载器)

    • 父加载器为Bootstrap ClassLoader
    • 加载JAVA_HOME/lib/ext目录下的类库
    • 或者加载java.ext.dirs的系统属性指定目录的类库
    • 父类是java.lang.ClassLoader

    1.3 App ClassLoader(应用加载器)

    • 父加载器为ExtClassLoader
    • 从java的classpath或者系统属性java.class.path所指定的目录加载类
    • 自定义ClassLoader的默认父加载器
    • 父类是java.lang.ClassLoader
    //JVM测试类
    public class JVMTest {
       public static void main(String[] args) {
           //JVMTest.class是在classpath下
           //由AppClassLoader加载
           System.out.println(JVMTest.class.getClassLoader());
       }
    }
    
    
    //运行结果:
       sun.misc.Launcher$AppClassLoader@73d16e93
    
    

    2.ClassLoader初始化源码剖析

    Launcher类是JVM的入口

    public class Launcher {
        //bootstrap ClassLoader加载路径
        private static String bootClassPath = System.getProperty("sun.boot.class.path");
    
        //构造函数
        public Launcher() {
            //扩展ClassLoader 初始化
            Launcher.ExtClassLoader var1;
            try {
                var1 = Launcher.ExtClassLoader.getExtClassLoader();
            } catch (IOException var10) {
                throw new InternalError("Could not create extension class loader", var10);
            }
    
            try {
                //应用ClassLoader初始化
                this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
            } catch (IOException var9) {
                throw new InternalError("Could not create application class loader", var9);
            }
    
            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);
            }
    
        }
    
        //bootstrap classloader并没有具体的实例,而是JVM实现的一部分
        private static class BootClassPathHolder {
            static final URLClassPath bcp;
        }
    
        //应用ClassLoader
        static class AppClassLoader extends URLClassLoader {
            //...
        }
    
        //扩展ClassLoader
        static class ExtClassLoader extends URLClassLoader {
            private static volatile Launcher.ExtClassLoader instance;
            //...
        }
    
    }
    
    

    3.ClassLoader双亲委托机制源码剖析

    public abstract class ClassLoader {
    
        //父加载器
        private final ClassLoader parent;
    
        //当前ClassLoader加载的类列表
        private final Vector<Class<?>> classes = new Vector<>();
    
        //构造函数 
        private ClassLoader(Void unused, ClassLoader parent) {
            //父加载器赋值
            this.parent = parent;
            if (ParallelLoaders.isRegistered(this.getClass())) {
                //类已注册
                parallelLockMap = new ConcurrentHashMap<>();
                package2certs = new ConcurrentHashMap<>();
                domains =
                    Collections.synchronizedSet(new HashSet<ProtectionDomain>());
                assertionLock = new Object();
            } else {
                parallelLockMap = null;
                package2certs = new Hashtable<>();
                domains = new HashSet<>();
                assertionLock = this;
            }
        }
    
        /**
         * 通过名称加载类
         * @param name 类全名
         */
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);
        }
    
        /**
         * 通过名称加载类
         * @param name 类全名
         * @param resolve 是否解析
         */
        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                //判断类是否被加载,最终调用的是native方法
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            //如果父ClassLoader不为null,
                            //先通过父ClassLoader加载类
                            c = parent.loadClass(name, false);
                        } else {
                            //如果父ClassLoader为null
                            //通过BootstrapClassloader来加载类
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
    
                    }
    
                    if (c == null) {
                        //如果父ClassLoader加载不到类
                        //那么当前ClassLoader继续加载
                        long t1 = System.nanoTime();
                        c = findClass(name);
    
                        //记录加载时间和数目
                       sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                       sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    
        //通过类名获取锁
        protected Object getClassLoadingLock(String className) {
            Object lock = this;
            if (parallelLockMap != null) {
                Object newLock = new Object();
                lock = parallelLockMap.putIfAbsent(className, newLock);
                if (lock == null) {
                    lock = newLock;
                }
            }
            return lock;
        }
    }
    
    

    4.双亲委托机制的优势

    • 防止类被重复加载
    • 防止JVM核心类被覆盖

    二.自定义ClassLoader

    1. ClassLoader抽象类中中几个重要方法

    • protected Class<?> loadClass(String name, boolean resolve)

      • 通过类名加载类
      • 调用了findLoadedClass()方法,如果返回null,那么递归找父ClassLoader,调用器loadClass()方法
      • 如果最终Bootstrap ClassLoader也没有成功加载Class,就会调用当前ClassLoader的findClass方法,去查找和加载类
      • protected方法,鼓励重写
    • protected Class<?> findClass(String name,boolean resolve)

      • 通过名称查找对应的类
      • 如果为true的花,在类加载之后,会对类进行解析
      • 如果类已经被加载,那么返回类的Class
      • 自定义ClassLoader实现该方法,可以配合defineClass()来使用
      • 抛出ClassNotFoundException异常
      • protected方法,鼓励重写
    • protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)

      • 将字节码的byte数组转换为对应的Class实例
      • final方法,不可以被重写
    //YGX自定义ClassLoader
    public class YgxClassloader extends ClassLoader {
        /**
         * 查找并加载类
         * 
         * @param name 类全名
         */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] data = loadData(name);
            return defineClass(name, data, 0, data.length);
        }
    
        // 获取数据
        private byte[] loadData(String name) {
            File file = new File("jvm" + File.separator + name + ".class");
            byte[] data = null;
            try {
                InputStream is = new FileInputStream(file);
                data = new byte[is.available()];
                is.read(data, 0, data.length);
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return data;
        }
    
        public static void main(String[] args) throws ClassNotFoundException {
            YgxClassloader ycl = new YgxClassloader();
            Class<?> clazz = ycl.loadClass("Hello");
            System.out.println(clazz.getName());
            System.out.println(clazz.getClassLoader());
        }
    
    }
    
    
    //运行结果:
        Hello
        jvm.YgxClassloader@15db9742
    
    

    2. thread.getContextClassLoader()和class.getClassLoader()

    • 由于双亲委托机制,我们可以得出
      • 父加载器加载的类,可以被子加载器获取和使用
      • 子加载器加载的类,无法被父加载器获取和使用
    • 某些服务(例如tomcat服务器)会使用自定义的ClassLoader去加载内部的类和资源
    • thread.getContextClassLoader()可以获取到服务使用的自定义类加载器
    • class.getClassLoader()获取到加载该clas的类加载器
    • class.getClassLoader()可能获取到thread.getContextClassLoader()的父加载器,从而导致无法加载服务需要的类

    相关文章

      网友评论

          本文标题:ClassLoader类加载器(四):双亲委托机制

          本文链接:https://www.haomeiwen.com/subject/afnegktx.html