美文网首页
自定义类加载器

自定义类加载器

作者: M_lear | 来源:发表于2020-09-20 21:51 被阅读0次

    相关类:ClassLoader
    相关方法:findClass;defineClass(将字节数组变成Class对象);loadClass;
    【双亲委派模型】使用模板模式实现在 loadClass 方法中。
    loadClass 的源码:

        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);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long 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;
            }
        }
    

    自定义类加载器两种实现:

    1. 遵守双亲委派模型:继承 ClassLoader,重写 findClass 方法。
    2. 不遵守双亲委派模型:继承 ClassLoader,重写 loadClass 方法。

    一、遵守双亲委派模型

    1.1 自定义类加载器

    代码:

    package classloader;
    
    import java.io.*;
    
    public class MyClassLoader extends ClassLoader {
        private String classPath;
        public MyClassLoader(String path) {
            classPath = path;
        }
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            // TODO Auto-generated method stub
            // 参数 name 为类的全限定名,作为 defineClass 方法的参数
            // 第一步:将 class 文件读入字节数组
            FileInputStream in = null;
            ByteArrayOutputStream out = null;
            byte[] buffer = new byte[1024];
            int len;
            try {
                in = new FileInputStream(classPath);
                out = new ByteArrayOutputStream();
                while((len = in.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            byte[] classByteArray = out.toByteArray();
            
            // 第二步:调用 defineClass 将字节数组变成 Class 对象
            return this.defineClass(name, classByteArray, 0, classByteArray.length);
        }
        
    }
    

    1.2 测试

    待加载类:

    package code;
    
    public class Reverse{
        public static void main(String[] args){
            int i = 0, j = args.length-1;
            while(i < j){
                String temp = args[i];
                args[i] = args[j];
                args[j] = temp;
                i++;
                j--;
            }
            for(i = 0; i < args.length; ++i){
                System.out.print(args[i]+" ");
            }
        }
    }
    

    实现了一个数组反转。

    测试类:

    package test;
    
    import java.lang.reflect.InvocationTargetException;
    import classloader.MyClassLoader;
    
    public class Main{
        public static void main(String[] args) throws Exception {
            MyClassLoader mcl = new MyClassLoader("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class");
            Class<?> r = mcl.loadClass("code.Reverse");
            System.out.println(r.getClassLoader());
            System.out.println(r.getName());
            r.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
        }
    }
    

    首先测试是否遵守双亲委派模型:
    当前工程和桌面都有 Reverse.class。
    测试结果:


    image.png

    可以看到,应用类加载器将当前工程下的 Reverse.class 加载进来。
    符合预期,遵守了双亲委派模型。

    删除当前工程下的 Reverse.class,看是否能加载桌面上的 Reverse.class。
    运行结果:


    image.png

    当所有父加载器都加载失败后,我们的自定义类加载器起作用了,成功将桌面上的 Reverse.class 加载进来。

    二、不遵守双亲委派模型

    当我们需要同时把当前工程和桌面的 Reverse.class 都加载进内存时,就需要定义一个不遵守双亲委派模型的类加载器。

    package classloader;
    
    import java.io.*;
    
    public class MyClassLoader1 extends ClassLoader {
        private String classPath, className;
        public MyClassLoader1(String path, String name) {
            classPath = path;
            className = name;
        }
    
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            // TODO Auto-generated method stub
            // 依赖类调用 super 的方法加载
            if(!name.equals(className)) return super.loadClass(name);
            
            // 将 class 文件读入字节数组
            FileInputStream in = null;
            ByteArrayOutputStream out = null;
            byte[] buffer = new byte[1024];
            int len;
            try {
                in = new FileInputStream(classPath);
                out = new ByteArrayOutputStream();
                while((len = in.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            byte[] classByteArray = out.toByteArray();
            
            // 调用 defineClass 将字节数组变成 Class 对象
            return this.defineClass(name, classByteArray, 0, classByteArray.length);
        }
        
    }
    

    测试类:

    package test;
    
    import classloader.MyClassLoader;
    import classloader.MyClassLoader1;
    
    public class Main{
        public static void main(String[] args) throws Exception{
            MyClassLoader mcl = new MyClassLoader("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class");
            Class<?> r = mcl.loadClass("code.Reverse");
            System.out.println(r.getClassLoader());
            System.out.println(r.getName());
            r.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
            
            System.out.println();
            System.out.println("--------------------------");
            
            MyClassLoader1 mcl1 = new MyClassLoader1("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class", "code.Reverse");
            Class<?> r1 = mcl1.loadClass("code.Reverse");
            System.out.println(r1.getClassLoader());
            System.out.println(r1.getName());
            r1.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
            
            System.out.println();
            System.out.println("--------------------------");
            
            System.out.println(r.newInstance() instanceof code.Reverse);
            System.out.println(r1.newInstance() instanceof code.Reverse);
        }
    }
    

    运行结果:


    image.png

    相关文章

      网友评论

          本文标题:自定义类加载器

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