Java类加载器解读

类加载器模型

    在Java中,类加载器一共有四种,分别是引导类加载器BootStrap ClassLoader、扩展类加载器Extension ClassLoader、应用类加载器Application ClassLoader(或者称为系统类加载器System ClassLoader )、用户自定义类加载器。其中前面三个是由JVM自身定义的,一定存在,用户自定义类加载器是程序开发人员根据业务需求编写的类加载器,不一定存在。
    启动类加载器Bootstrap ClassLoader 是由C++语言实现的,是虚拟机自身的一部分;除了启动类加载器Bootstrap ClassLoader,所有的ClassLoader都是继承于java.lang.ClassLoader来实现的。关系如下图:

Java类加载器解读_第1张图片

    分析这些类加载器,就绕不开java.lang.ClassLoader这个抽象类。我们先看下这个抽象类。

  1. 在该接口中定义了一个ClassLoader类型的属性Parent,这个属性决定了双亲委派机制的基础,当需要加载一个类的时候先委托给父类加载,层层递进。
    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;
  1. 在java.lang.ClassLoader中有一个protected 方法loadClass,这个就是双亲委派机制实现的源码,如下:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 先检查这个class是否已经被加载到内存中
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //双亲委派机制的实现原理,如果当前类加载器的父类不为空,就让父类去加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    		//如果父类加载器为空,则委托给引导类加载器,而该方法最终为native方法private native Class findBootstrapClass(String name);,实际上就是调用openjdk中BootStrap ClassLoader的实现去加载该类
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 如果仍然没有找到class,由当前的类加载器进行加载
                    long t1 = System.nanoTime();
                    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) {
            		//是否解析当前加载的class
                resolveClass(c);
            }
            return c;
        }
    }

    当前ClassLoader去加载class时,首先判断其“parent”属性的类加载器,如果不为null,则首先让“parent”类加载器去加载,这样按照“类加载委派关系图”一层层往上推;如果,其委派层次上面的“parent”类加载器加载失败,最后由当前的类加载器去加载。这里需要注意的是,虽然用户自定义ClassLoader的“parent”属性指向AppClassLoder,AppClassLoder的“parent”属性指向ExtClassLoder,但是ExtClassLoder的“parent”属性并不是指向Bootstrap ClassLoder,而是为null,当然Bootstrap ClassLoder的“parent”也为null。

类加载器源码

    扩展类加载器Extension ClassLoader和应用类加载器Application ClassLoader对应的都是rt.jar下sun.misc.Launcher.class中的两个静态内部类ExtClassLoader和AppClassLoader,两个类以组合的形式存在于Launcher类中;而引导类加载器BootStrap ClassLoader因为是使用C++实现,因此jdk中没有特定的类与之对应,
1. 引导类加载器BootStrap ClassLoader
引导类加载器加载路径为sun.boot.class.path
2. 扩展类加载器Extension ClassLoader
扩展类加载器Extension ClassLoader加载路径为java.ext.dirs,它的父类ClassLoader为空。
    static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;
        //获取ExtClassLoader 
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
                    if (instance == null) {
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }
        //创建ExtClassLoader 
        private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                   		//获取ExtClassLoader加载器加载路径下的所有class文件
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                        int var2 = var1.length;
                        for(int var3 = 0; var3 < var2; ++var3) {
                            MetaIndex.registerDirectory(var1[var3]);
                        }
                        //这里是真正生成ExtClassLoader的地方
                        return new Launcher.ExtClassLoader(var1);
                    }
                });
            } catch (PrivilegedActionException var1) {
                throw (IOException)var1.getException();
            }
        }

        void addExtURL(URL var1) {
            super.addURL(var1);
        }
        //ExtClassLoader的构造函数,通过跟踪父类源码可以看到第二个参数即为ClassLoader中的parent属性,这里为空,说明ExtClassLoader的父类加载器为空
        public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
        //加载指定路径的class文件
        private static File[] getExtDirs() {
        //可以看到ExtClassLoader加载路径为java.ext.dirs
            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;
        }

        private static URL[] getExtURLs(File[] var0) throws IOException {
            Vector var1 = new Vector();

            for(int var2 = 0; var2 < var0.length; ++var2) {
                String[] var3 = var0[var2].list();
                if (var3 != null) {
                    for(int var4 = 0; var4 < var3.length; ++var4) {
                        if (!var3[var4].equals("meta-index")) {
                            File var5 = new File(var0[var2], var3[var4]);
                            var1.add(Launcher.getFileURL(var5));
                        }
                    }
                }
            }

            URL[] var6 = new URL[var1.size()];
            var1.copyInto(var6);
            return var6;
        }
    }
  1. 应用类加载器Application ClassLoader
    应用类加载器Application ClassLoader加载路径为java.class.path,即classpath,当前工程的bin目录。它的父类ClassLoader为扩展类加载器Extension ClassLoader。应用类加载器Application ClassLoader加载路径为java.class.path,即classpath,当前工程的bin目录。它的父类ClassLoader为扩展类加载器Extension ClassLoader。
    static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
        //获取AppClassLoader 类加载器
        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        	  //可以看到加载路径为java.class.path
            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);
                }
            });
        }
        //AppClassLoader构造器,var2为他的父类加载器,在Launcher初始化AppClassLoader时可以看到var2为Extension ClassLoader
        AppClassLoader(URL[] var1, ClassLoader var2) {
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }
        //重写了ClassLoader的loadClass方法,加载当前目录的class
        public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (var2) {
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);
            }
        }
    }

类加载器初始化

ExtClassLoader和AppClassLoader均为sun.misc.Launcher.class中的两个静态内部类,初始化均在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实例传入进去作为AppClassLoader的parent加载器,与上文AppClassLoader源码对应
        	  //初始化AppClassLoader完成,并将this.loader指向它,这样每次加载都会从AppClassLoader开始
            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);
        }
    }

实现自己的类加载器

    类加载器一共有四种,其中前三种都是系统定义好的,最后一种用户自定义类加载器就是我们需要自己实现的。
实现自定义类加载器其实很简单,我们阅读ClassLoader的源码的loadClass方法就会看到,真正加载的方法是findClass方法。我们可以看一下这个方法,没有具体实现,只抛了一个异常,而且是protected的,这充分证明了:这个方法就是给开发者重写用的。源码如下:

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

自己实现只需要三步。
(1)继承ClassLoader (2)重写findClass()方法 (3)调用defineClass()方法
代码如下:

import java.io.*;

public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println("---------------------");
        System.out.println(System.getProperty("java.ext.dirs"));//输出ExtClassLoader的加载目录
        System.out.println(System.getProperty("java.class.path"));//输出AppClassLoader的加载目录
        System.out.println(ClassLoader.getSystemClassLoader());//输出AppClassLoader
        System.out.println(ClassLoader.getSystemClassLoader().getParent());//输出AppClassLoader的父类加载器ExtClassLoader
        System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());//此处为空,证明ExtClassLoader的父类加载器为空
        System.out.println("---------------------");
        MyClassLoader myClassLoader = new MyClassLoader("E:\\project\\common\\util\\target\\classes");
        try {
            Class c = myClassLoader.findClass("com.kyw.common.domain.SelectDomain");
            if(c!=null){
                Object obj=c.newInstance();
                System.out.println(obj.toString());
                System.out.println(c.getClassLoader().toString());
            }
            System.out.println("---------------------");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("找不到类");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
class MyClassLoader extends ClassLoader
{
    private String classPath ;
    public MyClassLoader() { }

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }
    public MyClassLoader(ClassLoader parent)
    {
        super(parent);
    }
    //重写findClass方法
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
        try {
            byte[] bytes = getClassBytes(name);
            //defineClass方法将字节码转化为类
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    //获取类的字节码
    private byte[] getClassBytes(String className) throws Exception
    {
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            String path=classPath + File.separatorChar +
                    className.replace('.',File.separatorChar)+".class";
            File file = new File(path);
            // 这里要读入.class的字节,因此要使用字节流
            in = new FileInputStream(file);
            out = new ByteArrayOutputStream();
            byte[] buffer=new byte[2048];
            int len=0;
            while((len=in.read(buffer))!=-1){
                out.write(buffer,0,len);
            }
            return out.toByteArray();
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally{
            in.close();
            out.close();
        }
        return null;
    }
}

输出结果为:

---------------------
C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\project\common\util\target\test-classes;E:\project\common\util\target\classes;E:\localRepository\junit\junit\4.11\junit-4.11.jar;E:\localRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;E:\localRepository\redis\clients\jedis\2.7.3\jedis-2.7.3.jar;E:\localRepository\org\apache\commons\commons-pool2\2.3\commons-pool2-2.3.jar;E:\localRepository\org\springframework\spring-beans\4.0.5.RELEASE\spring-beans-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-context\4.0.5.RELEASE\spring-context-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-aop\4.0.5.RELEASE\spring-aop-4.0.5.RELEASE.jar;E:\localRepository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;E:\localRepository\log4j\log4j\1.2.17\log4j-1.2.17.jar;E:\localRepository\org\slf4j\slf4j-api\1.7.2\slf4j-api-1.7.2.jar;E:\localRepository\org\slf4j\slf4j-log4j12\1.7.2\slf4j-log4j12-1.7.2.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-core\2.4.3\jackson-core-2.4.3.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-annotations\2.4.3\jackson-annotations-2.4.3.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-databind\2.4.3\jackson-databind-2.4.3.jar;E:\localRepository\mysql\mysql-connector-java\8.0.12\mysql-connector-java-8.0.12.jar;E:\localRepository\com\google\protobuf\protobuf-java\2.6.0\protobuf-java-2.6.0.jar;E:\localRepository\org\springframework\spring-core\4.0.5.RELEASE\spring-core-4.0.5.RELEASE.jar;E:\localRepository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;E:\localRepository\org\springframework\spring-tx\4.0.5.RELEASE\spring-tx-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-jdbc\4.0.5.RELEASE\spring-jdbc-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-context-support\4.0.5.RELEASE\spring-context-support-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-web\4.0.5.RELEASE\spring-web-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-webmvc\4.0.5.RELEASE\spring-webmvc-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-expression\4.0.5.RELEASE\spring-expression-4.0.5.RELEASE.jar;E:\localRepository\javax\servlet\javax.servlet-api\3.1.0\javax.servlet-api-3.1.0.jar;E:\localRepository\org\mybatis\mybatis\3.3.1\mybatis-3.3.1.jar;E:\localRepository\org\mybatis\mybatis-spring\1.2.5\mybatis-spring-1.2.5.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar;C:\Users\Administrator\.IntelliJIdea2018.2\system\captureAgent\debugger-agent.jar
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@b065c63
null
---------------------
com.kyw.common.domain.SelectDomain@5479e3f
com.kyw.MyClassLoader@490d6c15
---------------------

总结

注意类加载器的体系并不是“继承”体系,而是一个“委派”体系。大多数类加载器首先会到自己的parent中查找类或者资源,如果找不到,才会在自己 的本地进行查找。事实上,类加载器被定义加载哪些在parent中无法加载到的类,这样在较高层级的类加载器上的类型能够被“赋值”为较低类加载器加载的类型。

你可能感兴趣的:(Java虚拟机)