ClassLoader简介

1.什么是Classloader

我们知道,我们写的java程序会被编译器编译成class文件运行在jvm虚拟机上的,Classloader就是jvm加载class文件的工具。

当我们new ClassName(),或者使用Class.forName("包路径+类名"),或者使用classloader.loadclass("包路径+类名")的时候,就会触发Classloader去类加载对应的路径去查找*.class,并创建对象。

正常情况下, 一个classLoader需要两个必要属性:
parent: 用于指明当前classLoader的父类加载器.
url: 类命名空间, 用于指明当前classLoader从哪里加载class文件.

2.Classloader的分类

2.1 BootstrapClassloader 启动类加载器

其实类加载器也是java类,也需要被加载。BootstrapClassloader就是最初加载Classloader的加载器。而BootstrapClassloader本身,是JVM启动时自动加载,是被C++启动的。
BootstrapClassloader 负责加载JDK中的核心类库,主要是JAVA_HOME/lib下的类,如rt.jar、resources.jar、charsets.jar等,这里包括其他的java自带的类加载器。

2.2 ExtClassloader 扩展类加载器

主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar包。

2.3 AppClassloader 应用类加载器

主要负责加载classpath下的类,这里包括用户自己定义的类以及jar包。

2.4 自定义的类加载器

继承Classloader类实现自己的类加载器,默认父加载器AppClassloader。
这里的父加载器,并不是继承关系

3.Classloader的双亲委派

3.1什么是双亲委派

当一个类需要被加载时,当前的类加载器会把加载任务委派给parent加载器,parent加载器又会把加载任务委派给自己parent加载器,以此类推,
直到最顶层bootStrapClassLoader为止, 如果bootStrapClassLoader在自己的类空间(可以理解成上面提到的url)找到了该类的class文件, 就会去加载该类到内存中. 如果找不到, 会把任务再向下传递回extClassLoader, 让它去尝试加载该类. 依次类推, 直到某个类加载器在自己的类命名空间里找到了该类的class文件, 就会把该类加载进内存。


ClassLoader简介_第1张图片
classLoader
3.2双亲委派机制的用处

双亲委派机制其实是java的一种安全机制,能保证类在内存中只存在一份,所有程序使用的都是这一份, 不会出现自己定义一个String类, 被自己定义的classLoader给加载进来的情况, 因为rt.jar中的String会被最优先加载.

4.自定义Classloader

4.1实现方式

继承Classloader类,然后重写findClass方法,方法内去对应的路径下查找class文件,最后io读入,用defineClass把byte[] 转化为class对象。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class DqClassLoader extends ClassLoader {

    private String rootUrl;

    public DqClassLoader(String rootPath) {
        this.rootUrl = rootPath;
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        //根据类的二进制名称,获得该class文件的字节码数组
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        //将class的字节码数组转换成Class类的实例
        clazz = defineClass(name, classData, 0, classData.length);
        return clazz;
    }

    private byte[] getClassData(String name) {
        InputStream is = null;
        try {
            String path = classNameToPath(name);
            is = new FileInputStream(path);
            byte[] buff = new byte[1024 * 4];
            int len = -1;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((len = is.read(buff)) != -1) {
                baos.write(buff, 0, len);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private String classNameToPath(String name) {
        return rootUrl + "/" + name.replace(".", "/") + ".class";
    }
}
4.2 自定义加载器的应用场景

最常见的应用就是 Apache Tomcat 中的WebClassLoader,每个 Web 应用都有一个对应的类加载器实例。所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。
当然,Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。

5.结尾

类加载器是 Java 语言的一个创新。它使得动态安装和更新软件组件成为可能。
在开发自己的类加载器的时候,需要注意与已有的类加载器组织结构的协调。

你可能感兴趣的:(ClassLoader简介)