自定义类加载器
为什么需要自定义类加载器?java提供的类加载器只能够到指定的目录完成相关类的加载,若我们想对自定义目录下的类完成加载,此时就需要自定义类加载器。
之前说过,类加载的过程中最后,执行到最后会调用ClassLoader的loadClass方法,那么我们可以通过自定义加载器继承ClassLoader类,使自定义的类加载器也具有loadClass方法,则调用自定义加载器的loadClass方法后也会遵循双亲委派机制,完成类的一系列传递加载过程(向上再向下)。又因为loadClass方法中的findClass方法并没有具体实现,我们继承的该方法当然也需要重写。总之,若我们不想打破既定的双亲委派机制,则只需要重写findClass方法即可。具体代码如下所示:
1 static class MyClassLoader extends ClassLoader { 2 private String rootDir;/*自定义类加载的查找class的路径*/ 3 4 /*指定该类加载器会查找的rootDir目录,和父加载器*/ 5 public MyClassLoader(String rootDir, ClassLoader parent){ 6 super(parent); 7 this.rootDir = rootDir; 8 9 } 10 11 /*指定该类加载器会查找的rootDir目录*/ 12 public MyClassLoader(String rootDir){ 13 this.rootDir = rootDir; 14 } 15 16 17 @Override 18 protected Class<?> findClass(String name) ; 19 }
第一步:创建路径作为成员变量,生成对应的构造方法。
第二部:重写findClass方法。该方法具体实现如下:(主要为两步,先将class文件读取到字节数组中,使用defineClass方法完成类的加载过程)
protected Class<?> findClass(String name) throws ClassNotFoundException { //<1>.根据类的全路径(包含包名)类名和放置的目录确定类文件的路径 String className = name.substring(name.lastIndexOf(".")+1)+ ".class"; String classFile = rootDir + className; FileInputStream fileInputStream = null; byte[] classData = null; try { //<2>.将class文件读取到字节数组 fileInputStream = new FileInputStream(new File(classFile));//与class文件进行关联 classData = new byte[fileInputStream.available()];//开辟一个与class文件等大小的字节数组用于读取 fileInputStream.read(classData,0,classData.length);//将字节码读入字节数组 //<3>.将字节数据创建一个class return defineClass(name,classData,0,classData.length); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if (fileInputStream != null){ try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } //<4>如果父类加载器不是自定义的,上面的加载过程没加载成功,则此调用会throw ClassNotFoundException return super.findClass(name); }
第三步:编写测试方法
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { String rootDir = "D:/class/"; MyClassLoader classLoader = new MyClassLoader(rootDir); Class c = classLoader.loadClass("Person"); Object object = c.newInstance(); Method getNameMethod = c.getMethod("getName"); Method getAgeMethod = c.getMethod("getAge"); System.out.println("name:" + getNameMethod.invoke(object) + ",age:" + getAgeMethod.invoke(object)); System.out.println("类加载器为:" + object.getClass().getClassLoader()); }
运行结果如图所示: