自定义类加载器

  一、类加载器分类

  二、ClassLoader 的类加载机制

  三、自定义类加载器

一、类加载器分类

 类加载器作用: Java 程序在运行的时候,JVM 通过类加载机制(ClassLoader)把 class 文件加载到内存中,只有 class 文件被加载入内存,才能被其他的 class 引用,使程序正确运行起来

(1)Bootstrap ClassLoader

启动类加载器,负责加载 Java 基础类,对应的文件是 %JRE_HOME%/lib 目录下的 rt.jar 、resources.jar 、charsets.jar 、class 等

(2)Extension ClassLoader

Java 类,继承自 URLClassLoader 扩展类加载器,对应的文件是 %JRE_HOME%/lib/ext 目录下的 jar 和 class 等

(3)APP ClassLoader

Java 类,继承自URLClassLoader 系统类加载器,对应的文件是应用程序 classpath 目录下的所有 jar 和 class 等

 二、ClassLoader 的类加载机制

Java 的加载机制是双亲委派机制来加载类?

原因: 这是为了保证如果加载的类是一个系统类,那么会优先由Bootstrp ClassLoader 、Extension ClassLoader 先去加载,而不是使用我们自定义的 ClassLoader 去加载,保证系统的安全

三种类加载器存在父子关系,APP ClassLoader 的父类加载器是 Extension ClassLoader ,Extension ClassLoader 的父类加载器是 Bootstrap ClassLoader ,要注意的一点是这里并不是继承关系

当类加载器需要加载一个类时,会先委托它的父类加载器尝试加载,一直往上,如果最顶级的父类加载器没有找到改类,那么委托者自己加载

 只有被同一个类加载器实例加载并且文件名相同的class 文件才被认为是同一个class 

 三、自定义类加载器

 3.1 为何要自定义 ClassLoader

因为系统的 ClassLoader 只会加载指定目录下的 class 文件,如果想加载自己的 class 文件,就可以自定义一个 ClassLoader

3.2 如何自定义 ClassLoader 

 新建一个类继承 Java.lang.ClassLoader ,并重写 findClass 方法

public class MyClassLoader extends ClassLoader{

    /**
     * 指定路径
     */
    private String path;

    public MyClassLoader(String path) {
        this.path = path;
    }
    /**
     *
     * @param name 类的全路径名称
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class log = null;
        //获取改 class 文件字节码数组
        byte[] classData = getData();

        if(classData != null){
            log = defineClass(name,classData,0,classData.length);
        }
        return log;
    }

    /**
     * 将 class 文件转换为字节码数组
     */
    private byte[] getData(){
        File file = new File(path);
        if(file.exists()){
            FileInputStream in = null;
            ByteArrayOutputStream out = null;

            try {
                in = new FileInputStream(file);
                out = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int size = 0;
                while ((size = in.read(buffer)) != -1) {
                    out.write(buffer, 0, size);
                }
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                try{
                    in.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            return out.toByteArray();
        }else {
            return  null;
        }
    }
}

可以再getData 里做很多事情,比如加密解密之类的

创建一个class类

public class Log {

    public static void main(String []args){
        System.out.println("Hello,World");
    }
}

 使用自定义类加载器加载

public class MyClassMain {
    public static void main(String []args) throws Exception{


         String classPath = "D:\app\Log.class";

         MyClassLoader myClassLoader = new MyClassLoader(classPath);

         String packageNamePath = "com.Log";

         Class<?> myClass = myClassLoader.loadClass(packageNamePath);

         System.out.println("类加载器:"+myClass.getClassLoader());

         //利用反射获取方法
        Method method = myClass.getDeclaredMethod("main",String[].class);

        Object o = myClass.newInstance();

        String[] arg = {"ad"};

        method.invoke(o,(Object)arg);

    }
}

输出如下

ClassLoader类是一个抽象类,但却没有包含任何抽象方法。
如果要实现自己的类加载器且不破坏双亲委派模型,只需要继承ClassLoader类并重写findClass方法。
如果要实现自己的类加载器且破坏双亲委派模型,则需要继承ClassLoader类并重写loadClass,findClass方法。

参考:https://blog.csdn.net/huazai30000/article/details/85296671

参考:https://www.jianshu.com/p/f2ae00d64fa6

原文地址:https://www.cnblogs.com/baizhuang/p/12148432.html