美文网首页
深入 JVM 类加载器之自定义文件类加载器

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

作者: Loofer | 来源:发表于2018-04-21 23:14 被阅读0次

    自定义类加载器流程

    • 继承 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 类加载器之自定义文件类加载器

          本文链接:https://www.haomeiwen.com/subject/aogflftx.html