从HelloWorld聊一聊类加载器和双亲委派机制

众所周知,类加载主要是通过类加载器实现的,但是大多数人都是知其然,而不知其所以然,HelloWorld这个类大家都不陌生,从这个类开始聊一聊类加载器和双亲委派机制;

package com.fanqiechaodan.classloader;

/**
 * @date 2021/6/24 21:44
 * @declare
 */
public class HelloWorld {
    
    

    public static void main(String[] args) {
    
    
        System.out.println("HelloWorld");
    }
}

在这里插入图片描述

  1. 我们的类开始运行的时候,首先会先通过jvm.ddl文件创建jvm,就是我们的Java虚拟机;
  2. 创建一个引导类加载器bootstrapLoader,前两步都是通过C++实现的;
  3. 通过引导类加载器实例化一个Launcher类;负责加载创建其它类加载器
  4. 通过launcher.getClassLoader()创建AppClassLoader应用程序加载器,这个加载器是负责运行我们自己写的那些类
  5. appClassLoader.loadClass(“com.fanqiechaodan.classloader.HelloWorld”);加载我们要运行的类
  6. 执行main();此时HelloWorld就可以输出再我们的控制台了

Java里有以下几种类加载器:

  1. 引导类加载器(bootstrapLoader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar(Launcher就在tr这个jar里面)、resources.jar等
  2. 扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包
  3. 应用程序类加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那 些类(idea中target文件夹下的类)
  4. 自定义加载器:特殊情况下,需要使用我们自己写的类加载器;负责加载我们自定义路径下的类
package com.fanqiechaodan.classloader;

import sun.net.spi.nameservice.dns.DNSNameService;

/**
 * @date 2021/6/24 22:33
 * @declare
 */
public class MyClassLoader {
    
    

    public static void main(String[] args) {
    
    
        //获取Character的类加载器,Character是rt.jar下面的类
        System.out.println(Character.class.getClassLoader());
        //获取DNSNameService的类加载器,DNSNameService是dnsns.jar下面的类;dnsns.jar是ext扩展目录的jar
        System.out.println(DNSNameService.class.getClassLoader());
        //获取MyClassLoader的类加载器;MyClassLoader是我们自己写的类,属于target文件夹下面的
        System.out.println(MyClassLoader.class.getClassLoader());
        System.out.println();
        //获取应用程序加载器
        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        //获取应用程序加载器的父加载器:扩展类加载器
        ClassLoader extClassLoader = appClassLoader.getParent();
        //获取扩展类加载器:引导类加载器
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(appClassLoader);
        System.out.println(extClassLoader);
        System.out.println(bootstrapClassLoader);
    }
}
//输出结果:
null
sun.misc.Launcher$ExtClassLoader@3cbbc1e0
sun.misc.Launcher$AppClassLoader@18b4aac2

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@3cbbc1e0
null

从代码的输出结果上来看,三个类加载器是存在子父关系的,至于为什么bootstrapClassLoader为什么会输出一个null,这个我们再上面已经讲过了,引导类加载器是C++负责实例化的,我们再java代码中是看不到的,所以输出的是一个null值;并且还可以看出ExtClassLoader和AppClassLoader都是Launcher类下的内部类;我们从Launcher的源码中看一下,是不是这么回事.

public class Launcher {
    
    
    private static URLStreamHandlerFactory factory = new Launcher.Factory();
    //再类初始化时,就实例Launcher,方便再getLauncher()中返回,
    //单例设计模式
    private static Launcher launcher = new Launcher();
    private static String bootClassPath = System.getProperty("sun.boot.class.path");
    private ClassLoader loader;
    private static URLStreamHandler fileHandler;

    /*
     * 获取Launcher实例;
     */
    public static Launcher getLauncher() {
    
    
    	//直接返回launcher
        return launcher;
    }

    public Launcher() {
    
    
        Launcher.ExtClassLoader var1;
        try {
    
    
        	//首先获取一个ExtClassLoader扩展类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
    
    
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
    
    
        	//然后获取一个AppClassLoader,并且把刚刚获取到的ExtClassLoader当作参数传进去
            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);
        }

    }
    //....省略部分代码
    
    //Launcher.ExtClassLoader代码
      static class ExtClassLoader extends URLClassLoader {
    
    
          /*
           * 获取ExtClassLoader实例;
           */
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
    
    
            final File[] var0 = getExtDirs();

            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]);
                        }
						//再方法最后会直接实例化一个ExtClassLoader
                        return new Launcher.ExtClassLoader(var0);
                    }
                });
            } catch (PrivilegedActionException var2) {
    
    
                throw (IOException)var2.getException();
            }
        }
          void addExtURL(URL var1) {
    
    
            super.addURL(var1);
        }
          /*
           * ExtClassLoader构造方法;
           */
        public ExtClassLoader(File[] var1) throws IOException {
    
    
        	//实例化子类先调用父类的构造方法;parent传值是null
        	//bootstrapClassLoader是C++实现的,java代码传null
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
        
		 //....省略部分代码
		 
		 //Launcher.AppClassLoader代码
		   static class AppClassLoader extends URLClassLoader {
    
    
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
          /*
           * 获取AppClassLoader实例;
           * @Param  var0:就是ExtClassLoader
           */
        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    
    
        	//java.class.path,就是我们自己写的类路径
        	//再idea显示就是target文件夹
            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);
                    //再最后会实例化一个AppClassLoader,将ExtClassLoader当作参数
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }
		 /**
		  * AppClassLoader的构造方法
		  * @param var1 负载加载的路径地址数组
		  * @Param var2 ExtClassLoader
		  */
        AppClassLoader(URL[] var1, ClassLoader var2) {
    
    
        	//实例化子类先调用父类的构造方法;parent传值是ExtClassLoader
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

	//URLClassLoader的构造方法
	//AppClassLoader和ExtClassLoader都继承了URLClassLoader
	//再实例化的时候都会调用URLClassLoader的构造方法
	//ExtClassLoader调用时,parent的值传的是null或者说是bootstrapClassLoader
	//AppClassLoader调用时,parent的值传的是ExtClassLoader
	//子父级关系明显
	 public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
    
    
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
    
    
            security.checkCreateClassLoader();
        }
        acc = AccessController.getContext();
        ucp = new URLClassPath(urls, factory, acc);
    }

  1. Launcher.getLauncher(),返回的是再类初始化时就实例化的Launcher;属于单例模式
  2. Launcher的构造方法,会先调用Launcher.ExtClassLoader.getExtClassLoader()获取一个ExtClassLoader
  3. var1=Launcher.ExtClassLoader.getExtClassLoader()最后会直接实例化一个ExtClassLoader;ExtClassLoader继承URLClassLoader这个类;实例化子类会先调用父类的构造方法: super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);parent的值传的是null或者说是bootstrapClassLoader
  4. 接着会调用Launcher.AppClassLoader.getAppClassLoader(var1);获取一个AppClassLoader的实例;调用getAppClassLoader()时,会把ExtClassLoader当作参数传进去,同样的到最后也会实例化一个AppClassLoader,AppClassLoader也继承URLClassLoader这个类;实例化子类会先调用父类的构造方法: super(var1, var2, Launcher.factory);var1是AppClassLoader负责加载的类路径数组;var2就是parent这个值传的是ExtClassLoader.子父级关系明显
  5. 注意:这里面说的子父级关系不是继承关系,不是AppClassLoader继承ExtClassLoader,他们两个都继承URLClassLoader,子父关系说的是AppClassLoader的parent属性是ExtClassLoader;

最后说一下类加载的机制,双亲委派机制:
在这里插入图片描述
URLClassLoader继承ClassLoader这个类,我们的类加载主要是通过loadClass(String name)方法,接下来就直接看一下ClassLoader.loadClass(String name)方法的源码

    /**
   	 * 方法的重载
   	 * 我们调用的都是loadClass(String name)这个方法
   	 * 这个方法内部默认传入false调用它的重载方法
   	 * 
     * @param  name
     * @return 
     * @throws  ClassNotFoundException
     *          If the class could not be found
     */
public Class<?> loadClass(String name) throws ClassNotFoundException {
    
    
        return loadClass(name, false);
    }

    /**
     * 重载方法
     * 
     * @param  name 类的全限定类名
     * @param  resolve 是否解析,如果为true的话会解析这个类,默认传false,不再这里进行解析
     * @return 
     * @throws  ClassNotFoundException
     *          If the class could not be found
     */
    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();
                    //到最后都会调用URLClassLoader的findClass()
                    //在加载器的类路径里查找并加载该类
                    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) {
    
    
            	//默认false,永远不会执行
                resolveClass(c);
            }
            return c;
        }
    }
  1. 首先,检查一下当前的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
  2. 如果此类没有加载过,再判断一下父加载器是否为空(是否有父类加载器);如果不为空,则调用parent.loadClass(name, false)由父加载器加载,如果父加载器为空调用findBootstrapClassOrNull(name)由bootstrapClassLoader类加载器来加载.
  3. 如果父加载器及bootstrap类加载器都没有找到当前,那么调用当前类加载器的 findClass方法来完成类加载,最后将结果返回
  4. 双亲委派机制顺序:
    4.1 AppClassLoader
    4.2 ExtClassLoader
    4.3 bootstrapClassLoader
    4.4 ExtClassLoader
    4.5 AppClassLoader
  5. 总结:简单点说就是儿子先不加载,先找父亲加载,父亲不负责加载,再由儿子进行加载

为什么要有双亲委派机制?
避免类的重复加载,当儿子准备加载时,先让父亲去加载,父亲如果加载过了就直接返回,没有必要再让儿子再加载一遍;

Guess you like

Origin blog.csdn.net/qq_43135259/article/details/118197535