ClassLoader原理解析

一、概念

 

ClassLoader即类加载器,Java中的类是动态加载的,当需要使用类A时,ClassLoader会将A.class字节码文件动态加载到jvm的方法区内存中,然后生成一个对应的java.lang.Class对象的实例保存该类的信息,并通过该实例的newInstance()来产生对象。

ClassLoader是一个抽象类,通常给定类的名称,然后转换成.class文件名,然后查找相应的类文件进行加载。ClassLoader的实现类图如下:

二、常用的ClassLoader

 

JVM主要有三个类加载器

1)  BootstrapClassLoader:是JVM虚拟机的一部分,采用native代码编写,没有继承ClassLoader类,在虚拟机启动时它负责加载位于$JAVA_HOME/jre/lib/rt.jar等包中的类,包括java.lang.*/java.util.*等,还有java.net.Launcher,这个类内部包含两个静态子类,ExtClassLoaderAppClassLoader

BoostrapClassLoader加载类的路径为:

System.out.println(System.getProperty("sun.boot.class.path"));

输出:

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\resources.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\rt.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\sunrsasign.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jsse.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jce.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\charsets.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jfr.jar;

C:\ProgramFiles\Java\jdk1.8.0_92\jre\classes

2)  ExtClassLoader ClassLoader的实现类,用于加载扩展类,加载类的路径为

System.out.println(System.getProperty("java.ext.dirs"));

输出:

C:\Program Files\Java\jdk1.8.0_92\jre\lib\ext;

C:\Windows\Sun\Java\lib\ext

3) AppClassLoader ClassLoader的实现类,用于加载应用程序的主函数类和classpath下的包

System.out.println(System.getProperty("java.class.path"));

三、源码解析

1.首先看一下ClassLoader部分源码

1)ClassLoader中定义了父加载器变量parent,用户自定义类加载器时可以通过参数指定父加载器,默认采用AppLoaderClassLoader作为默认父加载器;

2)loadClass()方法用来加载类,首先调用父加载器的loadClass()方法,若父加载器为空,在调用BoostrapClassLoader加载。当父加载器加载失败时,才调用本身的findClass()方法来加载,这个方法在各加载器类中分别实现。

public class ClassLoader {
    //实现ClassLoader的类都有一个父加载器
    private final ClassLoader parent;

    //定义时由外部类指定父加载器
    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
            ...
    }

    //定义时由外部类指定父加载器
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    //默认的父加载器是AppClassLoader
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
    public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
            ...
        return scl;
    }
    private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                //Launcher获取的是AppClassLoader
                scl = l.getClassLoader();
                    ...
            }
            sclSet = true;
        }
    }

    //该方法由loadClass()调用,用于加载指定名字的类,由子类去实现,
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

    //加载类的方法
    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();
                //首先由父加载去加载类,如果不存在父加载器,则调用BootstrapClassLoader加载器
                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
                }

                //如果父加载器找不到类,则调用自己findClass加载类
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the 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();
                }
            }
            //如果resolve为true,则解析类
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}
2. Laucher的部分源码

1)Launcher是在JVM启动时由BoostrapClassLoader加载的,在构造函数中定义了AppClassLoader类实例 loader和ExtClassLoader类实例var2;

2)Launcher类定义了静态子类AppClassLoader,其构造函数中传入了var2,因此ExtClassLoader是其父加载器;

3)还定义了静态子类ExtClassLoader,其构造函数中parent传入null值,因此ExtClassLoader没有父加载器,但是ClassLoader默认的父加载器是BoostrapClassLoader,因此其父加载器为BoostrapClassLoader。

 

public class Launcher {
    private ClassLoader loader;
    public Launcher() {
        sun.misc.Launcher.ExtClassLoader var1;
        try {
            //创建ExtClassLoader的实例
            var1 = sun.misc.Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //创建AppClassLoader的实例作为默认的ClassLoader,构造函数中传入了ExtClassLoader的实例
            this.loader = sun.misc.Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        ...
    }
    public ClassLoader getClassLoader() {
        return this.loader;
    }

    //AppClassLoader是Launcher静态内部类
    static class AppClassLoader extends URLClassLoader {

        AppClassLoader(URL[] var1, ClassLoader var2) {
            //调用UrlClassLoader的构造函数,其中var2将作为parent加载器,因此AppClassLoader的父加载器是ExtClassLoader
            super(var1, var2, sun.misc.Launcher.factory);
            this.ucp.initLookupCache(this);
        }
        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] : sun.misc.Launcher.getClassPath(var1);
            return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction<sun.misc.Launcher.AppClassLoader>() {
                public sun.misc.Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : sun.misc.Launcher.pathToURLs(var2);
                    //调用AppClassLoader的构造函数
                    return new sun.misc.Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        //其中重写了loadClass方法
        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);
            }
        }
    }

    static class ExtClassLoader extends URLClassLoader {
        //调用URLClassLoader的构造函数,传入的父加载器为null,因此ExtClassLoader的父加载器为空
        public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, sun.misc.Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
    }
}

上面AppClassLoader和ExtClassLoader中调用了父类URLClassLoader的构造方法,其源码如下

 

public class URLCLassLoader {
    URLClassLoader(URL[] urls, ClassLoader parent,
                   AccessControlContext acc) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls);
        this.acc = acc;
    }
}

由以上分析得知,JVM的这种类加载顺序为Parent Delegation Model,其加载顺序如下图所示(借用其他人的图片)

 

四、自定义类加载器

1.步骤

1)自定义ClassLoader需要继承ClassLoader抽象类,可以在构造函数中指定其父加载器,如果不指定,默认是AppClassLoader;

2)复写findClass()方法;

3)在findClass方法中调用defineClass方法,defineClass方法ClassLoader方法已经提供了实现;

 

package com.lpq;

public class Test {

    public void say(){
        System.out.println("Say Hello");
    }
}
将Test.class放在E:/test文件夹下
public class MyClassLoader extends ClassLoader{
    private String myLibPath;
    public MyClassLoader(String myLibPath){
        this.myLibPath=myLibPath;
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        try {
            String fileName =name.substring(name.lastIndexOf(".")+1)+".class";
            FileInputStream is = new FileInputStream(new File(myLibPath,fileName));
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int b=0;
            try {
                while((b=is.read())!=-1){
                    bos.write(b);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] data = bos.toByteArray();
            is.close();
            bos.close();
            return defineClass(name,data,0,data.length);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            throw new ClassNotFoundException();
        }
    }
}
public class MainTest {
    public static void main(String[] args){
        MyClassLoader myClassLoader = new MyClassLoader("E:\\test");
        try {
            //加载class文件
            Class c = myClassLoader.loadClass("com.lpq.Test");

            if(c != null){
                try {
                    Object obj = c.newInstance();
                    Method method = c.getDeclaredMethod("say",null);
                    //通过反射调用Test类的say方法
                    method.invoke(obj, null);
                    System.out.println(obj.getClass().getClassLoader());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出:

Say Hello
com.lpq.MyClassLoader@3fb4f649

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值