Java classloader

文章目录

Classloader的继承性。

第一级叫做bootstrap classloader,加载JDK自带的类,也就是${JAVA_HOME}/lib下的类。

第二级叫做extern classloader,加载${JAVA_HOME}/lib/ext下的类。

第三级叫做system classloader,加载第三方的类,就是classpath里其他的类。

第四级及更高级叫做plugin classloader,是用户自己写的classloader或者new出来的URLClassLoader,加载classpath以外的类。在tomcat容器中,每个应用里的WEB-INF/lib和WEB-INF/classes就是通过tomcat自定义的classloader去加载的。

Classloader的隔离性。

  1. 父级不能调用子级的类。
    比如extern classloader里加载的class不能调用system classloader加载的类,会抛class not found异常.
  2. 同一级不同classloader加载的类不能互相调用。
    比如有两个plugin classloader, loader A 与loader B。loader A加载的类class A不能调用loader B加载的类 class B.
  3. 无法获取bootstrap Classloader的实例。
    System.out.println(String.class.getClassLoader());
    这个输出结果是null
    总而言之,只能child加载的类调用parent加载的类。兄弟及各种远房亲戚不能互相调用。

简单的plugin classloader

直接使用jdk自带的url classloader就可以加载任意jar包里的类了。

比如以下代码

java 复制代码
final URL url = new URL("file:///C:/Users/Ryan/IdeaProjects/learn/classloader/heap-1.0.jar");
final URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
final Class<?> heapClass = classLoader.loadClass("net.cloudsun.head.SmallHeap");
final Constructor<?> constructor = heapClass.getConstructor();
final Object o = constructor.newInstance();

final Method add = heapClass.getMethod("add", java.lang.Comparable.class);
for (int i = 0; i < 10; i++) {
    add.invoke(o, -i);
}
System.out.println(o);

自定义classloader

当不能提供url时,比如jar包不在磁盘里,也不在http服务器上,或者jar包是加密的。只能自己写classloader进行类的加载。

自己写classloader最终必须继承Classloader类。因为这个类有个保护型且final的defineClass方法,传入类的字节码,也就是byte[],就可以加载一个类。也就是说class的来源是byte[]。

比如

java 复制代码
public class JarClassLoader extends ClassLoader {

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 从jar包里取byte数组
        try {
            JarFile jarFile = new JarFile("heap-1.0.jar");
            final byte[] bytes = JarUtils.getByte(jarFile, name);
            if (bytes == null) {
                return super.loadClass(name);
            }
            return super.defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

上述代码的JarUtils是一个自己写的工具类。

java 复制代码
public static byte[] getByte(JarFile jarFile, String className) {

    final String entry = className.replace('.', '/') + ".class";
    final JarEntry jarEntry = jarFile.getJarEntry(entry);
    if (jarEntry == null) {
        return null;
    }
    try (final InputStream inputStream = jarFile.getInputStream(jarEntry)) {
        return IOUtils.toByteArray(inputStream);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }

}

当然这个classloader写得并不规范。因为规范的classloader首先要调用parent classloader去加载类,如果parent加载失败,再自己加载,而且加载的class必须缓存起来。因为从字节码加载class的开销是非常巨大的。当然前三级JDK自带的的classloader都会在loadClass方法里去检查class是否已经加载。所以只要每级class loader优先调用parent classloader,就可以自动实现class的缓存。

相关推荐
badhope1 小时前
Mobile-Skills:移动端技能可视化的创新实践
开发语言·人工智能·git·智能手机·github
码云数智-园园2 小时前
微服务架构下的分布式事务:在一致性与可用性之间寻找平衡
开发语言
吴佳浩2 小时前
GPU 编号进阶:CUDA\_VISIBLE\_DEVICES、多进程与容器化陷阱
人工智能·pytorch·python
C++ 老炮儿的技术栈3 小时前
volatile使用场景
linux·服务器·c语言·开发语言·c++
hz_zhangrl3 小时前
CCF-GESP 等级考试 2026年3月认证C++一级真题解析
开发语言·c++·gesp·gesp2026年3月·gespc++一级
大阿明3 小时前
Spring Boot(快速上手)
java·spring boot·后端
Liu628883 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
bearpping3 小时前
Java进阶,时间与日期,包装类,正则表达式
java
IT猿手3 小时前
基于控制障碍函数的多无人机编队动态避障控制方法研究,MATLAB代码
开发语言·matlab·无人机·openclaw·多无人机动态避障路径规划·无人机编队
全栈凯哥3 小时前
18.Python中的导入类完全指南
python