自定义类加载器
一、类加载器分类
二、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