美文网首页
如何写出自己的类加载器

如何写出自己的类加载器

作者: 探索的影子 | 来源:发表于2021-05-23 17:03 被阅读0次

    通过源码,我们可以看到类加载器最重要的就两个方法,一个是loadClass,另一个是findClass。loadClass实现了双亲委派机制。findClass实现了查找类,但是应用类加载器和扩展类加载器都没有实现这个功能,而是交给URLClassLoader实现。

    在下面的例子里面,我们重写一下findClass的过程

    package com.test.test;
    
    import java.io.FileInputStream;
    
    public class MyClassLoader extends ClassLoader{
        //读取文件
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.", "/");
            FileInputStream fis = new FileInputStream( "D:/test/" + name + ".class");
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                //读取字节数组
                byte[] bytes = loadByte(name);
                //定义类
                return defineClass(name,bytes,0,bytes.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static void main(String[] args) throws ClassNotFoundException {
            MyClassLoader myClassLoader = new MyClassLoader();
            Class<?> aClass = myClassLoader.loadClass("com.test.test.User");
            System.out.println(aClass.getClassLoader());
    
            Class<?> bClass = myClassLoader.loadClass("com.test.test.User1");
            System.out.println(bClass.getClassLoader());
        }
    }
    
    

    这是我的文件结构

    image-20210522160222860

    然后我在D盘放了User1和User两个类。

    执行结果
    "C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar=9658:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;F:\workspace\test\target\classes" com.test.test.MyClassLoader
    sun.misc.Launcher$AppClassLoader@18b4aac2
    com.test.test.MyClassLoader@1540e19d
    
    分析结果

    User类的类加载器是应用加载器。这是因为采用的是双亲委派机制,执行路线MyClassLoader->AppClassLoader->ExtClassLoader->BootstrapLoader->ExtClassLoader->AppClassLoader。

    User1类的类加载器是MyClassLoader。这是因为父加载器都没有,才轮到自己执行。

    如何打破双亲委派机制

    其实也不复杂,只需要重写一下loadClass里面的代码就可以实现。

    如果只是删除parent.loadClass是不够的。这是因为所有的类的父类都是Object。删除掉了parent.loadClass这意味着扩展类加载器和引导类加载器都不会进行加载。但是加载User和User1的前提,必须是先加载Object.class。所以还需要追加代码,完整代码如下:

     @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                long t1=0;
                if (c == null) {
                    long t0 = System.nanoTime();
                   //删除双亲委派这一部分代码就可以了
                    //追加这部分代码是为了解决java.io.FileNotFoundException: D:\test\java\lang\Object.class (系统找不到指定的路径。)
                    //如果不是指定类那么就走传统的双亲委派
                    if (!"com.test.test.User".equals(name)){
                        c=super.loadClass(name,resolve);
                    }else {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                       t1  = System.nanoTime();
                        c = findClass(name);
                    }
    
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    

    错误:

    java.io.FileNotFoundException: D:\test\java\lang\Object.class (系统找不到指定的路径。)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(FileInputStream.java:195)
        at java.io.FileInputStream.<init>(FileInputStream.java:138)
        at java.io.FileInputStream.<init>(FileInputStream.java:93)
        at com.test.test.MyClassLoader.loadByte(MyClassLoader.java:9)
        at com.test.test.MyClassLoader.findClass(MyClassLoader.java:21)
        at com.test.test.MyClassLoader.loadClass(MyClassLoader.java:42)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
        at com.test.test.MyClassLoader.findClass(MyClassLoader.java:22)
        at com.test.test.MyClassLoader.loadClass(MyClassLoader.java:42)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at com.test.test.MyClassLoader.main(MyClassLoader.java:58)
    Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
        at com.test.test.MyClassLoader.findClass(MyClassLoader.java:22)
        at com.test.test.MyClassLoader.loadClass(MyClassLoader.java:42)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at com.test.test.MyClassLoader.main(MyClassLoader.java:58)
    
    Process finished with exit code 1
    
    

    相关文章

      网友评论

          本文标题:如何写出自己的类加载器

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