深入 JVM 类加载器之自定义文件类加载器

自定义类加载器流程

  • 继承 java.lang.ClassLoader
  • 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回。
  • 委派类加载请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例。
  • 调用本类加载器的 findClass(…)方法,试图获取对应的字节码,如果获取的到,则调用 defineClass(…)导入类型到方法区;如果获取不到对应的字节码或者其他原因失败,返回异常给 loadClass(…)loadClass(…) 转抛异常,终止加载过程。

注意:被两个类加载器加载的同一个类,JVM不认为是相同的类

测试代码

  • 编写FileSystemClassLoader.java
package com.classloader.test;

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


public class FileSystemClassLoader extends ClassLoader {

    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {

        Class c = findLoadedClass(name);
        //应该先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载心得类。
        if (c != null) {
            return c;
        }else {
            ClassLoader parent = this.getParent();
            try {
                c = parent.loadClass(name);
            } catch (ClassNotFoundException e) {
                
            }
            if (c != null) {
                return c;
            }else {

                byte[] data = getClassData(name);
                if (data == null) {
                    throw new ClassNotFoundException();
                } else {
                    return defineClass(name, data, 0, data.length);
                }
            }
        }


    }

    private byte[] getClassData(String name) {
        //包名转成文件路径
        String path = pathForClassName(name);
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(path);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.flush();
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private String pathForClassName(String className) {
        return rootDir + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }

}
  • 在 D盘根目录创建 HelloWorld.java 代码如下
package com.classloader.test;

public class HelloWorld {
    public static void main(String[] args){
        System.out.println("xxx");
    }
}

编译生成 class 文件

  • 测试主类
package com.classloader.test;

public class FileClassLoaderTest {
    public static void main(String[] args){
        FileSystemClassLoader loader = new FileSystemClassLoader("D:/");
        FileSystemClassLoader loader2 = new FileSystemClassLoader("D:/");
        try {
            Class c1 = loader.loadClass("com.classloader.test.HelloWorld");
            Class c2 = loader.loadClass("com.classloader.test.HelloWorld");
            Class c3 = loader2.loadClass("com.classloader.test.HelloWorld");

            System.out.println(c1.hashCode());
            System.out.println(c2.hashCode());
            System.out.println(c3.hashCode());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出

21685669
21685669
2133927002

可以看出 c1 跟 c2 的 hashCode 一致,说明是同一个对象,而 c3 不同,这就印证了我们上面所说的:被两个类加载器加载的同一个类,JVM不认为是相同的类

你可能感兴趣的:(深入 JVM 类加载器之自定义文件类加载器)