美文网首页虚拟机
Java虚拟机--自定义类加载器

Java虚拟机--自定义类加载器

作者: 贾博岩 | 来源:发表于2018-05-26 10:56 被阅读96次

    如何定义一个类加载器

    前面,我们介绍了类加载器的原理,以及类加载的源码。

    本篇,我们结合前面所说的内容,来自定义一个类加载器,以及使用自定义的类加载来完成类加载操作。

    在展示代码之前,我们来探讨一个问题,为什么要自定义类加载?

    什么场景下需要我们来自定义类加载器加载我们所需要的类?

    (1)被加密的.class文件,为了安全保证你.class文件进行了加密处理,在程序运行的过程中,你需要解密后再进行操作。此时,就需要自定义一个类加载器来完成.class文件的解密操作,解密完成后再进行类加载;

    (2).class文件不在默认的类加载路径下(可参考之前的文章,看下类路径都包含哪些),如果想要加载则需要自己定义类加载来完成,例如:.class文件内容存放在了数据库、网络地址(ftp服务器)等;

    (3)对于非.class的文件,需要转为Java类,就需要自定义类加载器,例如:JSP文件。

    如果你还能想到别的场景,请在下面的评论中指出!!!

    代码展示

    通常情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则(为什么要覆盖此方法,前面的文章中有解答),取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象,简单例子如下:

    public class ClassLoaderTest extends ClassLoader{
    
        private String loaderRootPath;
    
        public ClassLoaderTest(ClassLoader parent) {
            super(parent);
        }
    
        public ClassLoaderTest(String loaderRootPath) {
            this.loaderRootPath = loaderRootPath;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try{
                String filePath = loaderRootPath + name.replace('.', File.separatorChar) + ".class";
                //指定读取磁盘上的某个文件夹下的.class文件:
                File file = new File(filePath);
                FileInputStream fis = new FileInputStream(file);
                byte[] bytes = new byte[fis.available()];
                fis.read(bytes);
                //此处可以对字节数组进行解密操作:
                //省略。。。。
                //调用defineClass方法,将字节数组转换成Class对象
                Class<?> clazz = this.defineClass(name, bytes, 0, bytes.length);
                System.out.println(clazz.getClassLoader());
                return clazz;
            }catch (FileNotFoundException e){
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    
        public static void main(String[] agrs) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
            try {
                ClassLoaderTest classLoaderTest = new ClassLoaderTest("d:/");
                classLoaderTest.loadClass("ObjectTest1");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    如果,你在开发工具(例如:idea)里创建的测试类,那么在编译完成后,一定要把classpath下的文件删除,否则类加载机制会使用应用类加载器进行类加载。

    测试结果如下:

    没删除classpath下的类的结果:sun.misc.Launcher$AppClassLoader@8fd9b4d
    
    删除classpath下的类的结果:ClassLoaderTest@280ae735
    

    对于自定义类加载,还有另一种更为简单的方案,就是继承URLClassLoader类。

    为什么说继承URLClassLoader类更为简单?(URLClassLoader源码讲解)

    在URLClassLoader类中,已经帮我们实现了获取字节数组的逻辑,并将字节数组转换成Class对象。

    public class ClassLoaderTest1 extends URLClassLoader {
    
        public ClassLoaderTest1(URL[] urls) {
            super(urls);
        }
    
        public ClassLoaderTest1(URL[] urls, ClassLoader parent) {
            super(urls, parent);
        }
    
        public ClassLoaderTest1(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
            super(urls, parent, factory);
        }
    
        public static void main(String[] agrs) {
            String rootDir="D:/";
            File file = new File(rootDir);
            URI uri=file.toURI();
            try {
                URL[] urls= new URL[]{uri.toURL()};
                ClassLoaderTest1 loader = new ClassLoaderTest1(urls);
                Class clazz = loader.loadClass("ObjectTest1");
                System.out.println(clazz.getClassLoader());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    测试结果:

    ClassLoaderTest1@6469adc7

    相关文章

      网友评论

        本文标题:Java虚拟机--自定义类加载器

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