开发者

Java中如何自定义一个类加载器

开发者 https://www.devze.com 2022-11-28 14:38 出处:网络 作者: 盛世如恋
目录如何自定义加载器?示例:读取某文件的下的某class文件类加载器的使用及自定义类加载器如何自定义加载器?1.创建一个自定义加载器类继承ClassLoader类2.重写findClass方...
目录
  • 如何自定义加载器?
    • 示例:读取某文件的下的某class文件
  • 类加载器的使用及自定义类加载器

    如何自定义加载器?

    1.创建一个自定义加载器类 继承 ClassLoader 类

    2.重写 findClass 方法。 主要是实现从那个路径读取 jar包或者.class文件,将读取到的文件用字节数组来存储,然后可以使用父类的 defineClass 来转换成字节码。

    如果想破坏双亲委派的话,就重写 loadClass 方法, 否则不用重写

    注意:

    1.ClassLoader提供的 protected final Class<?> defineClass(String name, byte[] b, int off, int len) 是用来将字节数组转换成字节码文件的,传入参数 是 (类名,字节数组数据,字节数组读取的开始下标,字节数组的长度)

    示例:读取某文件的下的某class文件

    创建一个名为MyClassLoader的类加载器:

    import Java.io.*;
    
    public class MyClassLoader extends ClassLoader{
        String path ;
        MyClassLoader(String dir){
            this.path = dir ;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                this.path = this.path + name +".class";
                File f = new File(path);
                InputStream in = new FileInputStream(f);
                byte [] bys = new byte[  (int)f.length() ];
                int len = 0;
                while( (len = in.read(bys) )!= -1  ){
    
                }
                // byte[] -> .class
                return defineClass(name,bys,0,bys.length);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    }

    测试:

    在如下目录,生成一个Hello.class字节码文件

    Hello.java:

    public class Hello {
        public void sayHello(){
            System.out.println("Hello World!");
        }
    }

    Java中如何自定义一个类加载器

    测试类:

    import java.lang.reflect.Method;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            MyClassLoader my = new MyClassLoader("D:\\test\\jvmtest\\");
            Class<?> c1 = my.loadClass("Hello");
            Object o = c1.newInstance();
            Method d = c1.getMethod("sayHello",null);
            d.invoke(o);
        }
    }

    运行测试类:

    Java中如何自定义一个类加载器

    类加载器的使用及自定义类加载器

    package com.tech.load.def;
     
    /**
     * @author lw
     * @since 2021/12/3
     */
    public class UserImpl {
        static {
            System.out.println("UserImpl init ...");
        }
    }
    package com.tech.load.def;
     
    /**
     * @author lw
     * @since 2021/12/3
     */
    public class DefLoader {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            //上下文类加载器,默认使用的是 应用程序类加载器
    //        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
    //        Class<?&g编程客栈t; c1 = contextClassLoader.loadClass("com.tech.load.def.UserImpl");
    //        c1.newInstance(); //classloader.loadClass 不会触发初始化,当创建对象时执行初始化,执行静态程序块内容 输出 "UserImpl init ..."
    //        ClassLoader contextClassLoader1 = Thread.currentThread().getContextClassLoader();
    //        Class<?> c2 = contextClassLoader1.loadClass("com.tech.load.def.UserImpl");
    //        c2.newInstance(); //使用相同的类加载器 加载相同的类名 则加载的是同一个类,c1 c2是同一个类,由于已经初始化过 创建对象不再初始化 不再打印  "UserImpl init ..."
    //        System.out.pripythonntln(contextClassLoader==contextClassLoader1); //true 获取的上下文类加载器是同一个类加载器 
    //        System.out.println(c1==c2); // true 同一个类加载器器,加载同名的类,第一次加载时加载的类会缓存到类加载器的缓存,再次加载直接在缓存读取,两次加载的是同一个类
            
            //直接获取类的类加载器 应用程序类加载器
            ClassLoader classLoader = UserImpl.class.getClassLoader();
           开发者_Go入门 ClassLoader classLoader1 = UserImpl.class.getClassLoader();
            System.out.println(classLoader==classLoader1); //true 获取的是同一个应用程序类加载器
           
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            System.out.println(classLoader==contextClassLoader); //true 线程上下文类加载器默认采用的也是应用程序类加载器 与通过类型对象getClassLoader获取方式相同
     
            Class<?> c1 = Class.forName("com.tech.load.def.UserImpl"); //会触发类的初始化
            ClassLoader classLoader2 = c1.getClassLoader();
            System.out.println(classLoader==classLoader2); //true 获取的是同一个应用程序类加载器
            
        }
    }

    在应用程序中,默认我们获取上下文类加载器、类型对象getClassLoader都是采用的同一个应用程序类加载器,类在第一次被加载后会缓存到类加载器的缓存中,由于是同一个类加载器此时同名的类不能被多次加载,且应用程序类加载器只能加载classpath下的类。

    如果我们想加载自定义路径下的类,需要用到自定义类加载器,可以去指定路径下加载类,且通过创建多个类加载器对象,加载的同名类相互隔离,也就是说同名类可以被多个自定义类加载器对象加载。

    编写自定义类加载器:

    • 继承ClassLoader;
    • 重写findClass方法在指定路径下进行类的加载,得到字节数组,然后使用defineClass根据字节数组生成字节码文件 也就是class文件;

    编写一个测试类Goods放在D盘下,javac得到class文件

     
    /**
     * @author lw
     * @since 2021/12/3
     */
    public class Goods {
        static {
            System.out.println("Goods init ...");
        }
    }

    javac Goods.java

    package com.tech.load.def;
     
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.nio.file.Files;
    impwww.devze.comort java.nio.file.Paths;
     
    /**
     * 自定义类加载器 加载类
     * @author lw
     * @since 2021/12/3
     */
    public class DefLoad7 {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            MyClassLoader classLoader1 = new MyClassLoader();
            Class<?> c1 = classLoader1.loadClass("Goods");
            Class<?> c2 = classLoader1.loadClass("Goods");
            System.out.println(c1==c2);//true 使用同一个类加载器加载同名类两次,实际只加载了一次,第二次是在类加载器的缓存加载的 结果两次加载的是同一个
            c1.newInstance(); //会初始化
            c2.newInstance(); //不会初始化 
            
            MyClassLoader classLoader2 = new MyClassLoader();
            Class<?> c3 = classLoader2.loadClass("Goods");
            System.out.println(c1==c3); //false 使用不同的类加载器对同一个类进行加载,会得到不同的类型对象
            c3.newInstance(); //会初始javascript化
        }
    }
     
    //自定义类加载器 加载D盘下的类
    class MyClassLoader extends ClassLoader{
        
        //去指定的路径下加载类
        //name是类名称
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String path="D:\\"+name+".class";
            try {
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                //将指定路径下的文件 拷贝到输出流
                Files.copy(Paths.get(path)http://www.devze.com,os);
                byte[] bytes = os.toByteArray();
                //调用父类的方法 根据字节数组生成字节码文件 也就是class文件
                //bytes -> *.class
     
                return defineClass(name,bytes,0,bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ClassNotFoundException("类文件未找到",e);
            }
        }
    }

    Java中如何自定义一个类加载器

    使用自定义加载器,创建多个类加载器对象去加载同一个类,会得到多个类型对象。 

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号