java ClassLoader源码剖析和双亲委派模型实现

ClassLoader是做什么的?

一个类的生命周期取决于它Class对象的生命周期,经历加载、连接、初始化、使用、和卸载五个阶段。ClassLoader负责将.class文件中的二进制数据加载到内存中,放到jvm的方法区中。

什么是双亲委派模型

在这里插入图片描述

一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。

类加载器分类

类加载器可以划分成三类:

1.启动类加载器(Bootstrap ClassLoader)

这个类加载器负责将\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库,此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用,代码是使用C++编写的.是虚拟机自身的一部分.

2.扩展类加载器(Extendsion ClassLoader):

这个类加载器负责加载\lib\ext目录下的类库,用来加载java的扩展库,开发者可以直接使用这个类加载器.

3.应用程序类加载器(Application ClassLoader):

这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.

jdk 双亲委派模型实现

ClassLoader源码剖析

所有类加载器都会继承自java.lang.ClassLoader类(除了类启动加载器是c++实现的)。双亲委派模型的实现主要是java.lang.ClassLoader#loadClass(java.lang.String, boolean)和java.lang.ClassLoader#findClass两个方法。
先来看下loadClass方法的实现:

    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 {
                	//先判断parent是否为空
                	//如果不为空,先尝试用父类加载器加载
                	//如果为空,则先尝试用类启动加载器加载(除了拓展类加载器parent为空,一般parent都不会为空,稍后解释)
                    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();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

再来看下findClass的实现:

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

可以看到,findClass的默认实现是抛出ClassNotFoundException异常,也就是说父类加载器加载不成功,就抛出了异常。

AppClassLoader和ExtClassLoader源码剖析

先看下AppClassLoader和ExtClassLoader的继承关系:
在这里插入图片描述
可以看出AppClassLoader和ExtClassLoader继承自URLClassLoader。URLClassLoader主要实现通过url路径加载类的功能。从另一个侧面也反映出。jdk的双亲委派模型是基于组合而非继承来实现的。
AppClassLoader和ExtClassLoader的实现都在sun.misc.Launcher类上。Launcher主要是在程序启动的时候加载主要的类加载器(Ext和App类加载器)。我们看下Launcher加载的代码:

    public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        // Also set the context class loader for the primordial thread.
        Thread.currentThread().setContextClassLoader(loader);

       	......
    }

	
	//ExtClassLoader的构造函数
    public ExtClassLoader(File[] dirs) throws IOException {
        super(getExtURLs(dirs), null, factory);
        SharedSecrets.getJavaNetAccess().
            getURLClassPath(this).initLookupCache(this);
    }
	
	//AppClassLoader构造函数
    AppClassLoader(URL[] urls, ClassLoader parent) {
       super(urls, parent, factory);
       ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
       ucp.initLookupCache(this);
    }

Launcher构造函数会创建ExtClassLoader实例和AppClassLoader实例,ExtClassLoader的创建也是调用父类的构造方法,但parent传的是null(parent是null代表父类是类启动加载器),AppClassLoader的构造则传入刚才创建的ExtClassLoader实例,因此他的parent是ExtClassLoader。
我们再来看下如果使用没有parent的构造函数创建类加载器是什么情况:

    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

可以看到如果没有使用带parent的构造函数,parent会指定为getSystemClassLoader方法返回的对象,实际上就是AppClassLoader。双亲委派模型的递归调用链就这样形成了。

回头再看loadClass和findClass方法

如果我们想自定义类加载器呢。如果我们不想破坏双亲委派模型,我们可以重写findClass方法。如果我们要破坏双亲委派模型,则需要重写loadClass方法。
做以下实验:

package classLoader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 测试自定义
 */
public class UserClassLoaderTest {

    /**
     * 重写findClass方法
     */
    static class UClassLoader1 extends ClassLoader {

        String path = "/Users/zhaozhenrong/Codes/workspace/my-test-project/";

        /**
         * 重写findClass
         *
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            File file = new File( path+name+".class");
            if (!file.exists()) {
                throw new ClassNotFoundException(name);
            }
            byte[] bytes = new byte[(int) file.length()];
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                fis.read(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return defineClass(name, bytes, 0, bytes.length);

        }

    }

    /**
     * 重写loadClass
     */
    static class UClassLoader2 extends ClassLoader {
        String path = "/Users/zhaozhenrong/Codes/workspace/my-test-project/";
        /**
         * 重写loadClass,破坏双亲委派模型
         *
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            try {
                File file = new File(path + name + ".class");
                if (!file.exists()) {
                    throw new ClassNotFoundException(name);
                }
                byte[] bytes = new byte[(int) file.length()];
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(file);
                    fis.read(bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

                return defineClass(name, bytes, 0, bytes.length);
            }catch (ClassNotFoundException ex){
                //如果加载不成功才调用父加载器
                return super.loadClass(name);
            }

        }
    }


    public static void main(String[] args) throws ClassNotFoundException {
        UClassLoader1 cl1 = new UClassLoader1();
        UClassLoader1 cl2 = new UClassLoader1();

        UClassLoader2 cl3 = new UClassLoader2();
        UClassLoader2 cl4 = new UClassLoader2();
        //1,重写defineClass,classpath中有对应classname
        Class<?> c1 = cl1.loadClass("CommomClass1");
        Class<?> c2 = cl2.loadClass("CommomClass1");

        //classpath有对应的classname,所以是由父classloader加载
        System.out.println(c1.equals(c2));

        //2,重写defineClass,classpath中没有对应classname
        Class<?> c3 = cl1.loadClass("CommomClass");
        Class<?> c4 = cl2.loadClass("CommomClass");

        //classpath没有对应的classname,所以最终由UClassLoader1加载
        //虽然加载同一个类,但是不同的加载起加载,也不相同
        System.out.println(c3.equals(c4));

        //3,重写loadclass
        Class<?> c5 = cl3.loadClass("CommomClass1");
        Class<?> c6 = cl4.loadClass("CommomClass1");

        //classpath有对应的classname,但重写loadclass破坏了双亲委派模型
        //所以由UClassLoader2加载
        System.out.println(c5.equals(c6));

    }
}

//结果
//true
//false
//false

最后

demo源码:https://github.com/rain-zhao/java-test

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值