写这个自定义类加载器是因为了解到热部署的原理,是通过打破了双亲委派模型的自定义类加载器来加载的,使用新的类加载器实例来加载新的类,然后替换掉方法区中的旧的类。
两个类,一个KaJong类,一个MyClassLoader类。直接调用findClass()方法是为了打破双亲委派模型,如果直接调用loadClass()方法,尽管loadClass()方法里面调用了findClass()方法,但这样加载出来的类并没有打破双亲委派模型。(因为loadClass方法进行了双亲委派的逻辑处理,优先将类交给父类进行加载)
public class KaJong {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 使用这个方法获取到的路径有问题,因为我的目录中包含有空格,可能是字符处理上有问题
// URL url = MyClassLoader.getSystemResource("KaJong.class");
// String file = url.getPath();
String file = System.getProperty("user.dir")+"\\target\\classes\\com\\example\\demo\\classloadertest\\KaJong.class";
System.out.println(file);
//通过自定义类加载器1加载的类对象
MyClassLoader myClassLoader1 = new MyClassLoader(file);
Class kajong1 = myClassLoader1.findClass("com.example.demo.classloadertest.KaJong");
//通过系统类加载器加载的类对象
Class kajong2 = KaJong.class;
//通过自定义类加载器2加载的类对象
MyClassLoader myClassLoader2 = new MyClassLoader(file);
Class kajong3 = myClassLoader2.findClass("com.example.demo.classloadertest.KaJong");
System.out.println(kajong1.toString());
System.out.println(kajong2.toString());
System.out.println(kajong1.equals(kajong2));
System.out.println(kajong1.equals(kajong3));
System.out.println("kajong1's classloader:"+kajong1.getClassLoader().toString());
System.out.println("kajong2's classloader:"+kajong2.getClassLoader().toString());
System.out.println("kajong3's classloader:"+kajong3.getClassLoader().toString());
}
}
MyClassLoader类需要继承ClassLoader方法,重写findClass方法,通过调用defineClass方法来加载二进制字节码文件。注意这里的new byte[fi.available()]是必需的,哪怕只多一个空字节都会报错,使用fi.available()可以获取到文件流的字节长度。
public class MyClassLoader extends ClassLoader {
private String file;
public MyClassLoader(String file){
this.file = file;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = null;
byte[] a = null;
try {
// 绝对路径
// File file = new File("com.example.demo.create.KaJong");
// InputStream fi = new FileInputStream(new File("C:\\Users\\good luck\\Desktop\\learnJavaItem\\target\\classes\\com\\example\\demo\\classloadertest\\KaJong.class"));
InputStream fi = new FileInputStream(new File(file));
// System.out.println(fi.available());
a = new byte[fi.available()];
fi.read(a);
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
// System.out.println("name= " + name);
Class kaJong = defineClass(name,a,0,a.length);
// System.out.println(kaJong.toString());
return kaJong;
}
}
下面是输出结果,可以看到不同的类加载器加载的KaJong类,使用equals方法判断的结果也不相等。
C:\Users\good luck\Desktop\learnJavaItem\target\classes\com\example\demo\classloadertest\KaJong.class
class com.example.demo.classloadertest.KaJong
class com.example.demo.classloadertest.KaJong
false
false
kajong1's classloader:com.example.demo.classloadertest.MyClassLoader@7ba4f24f
kajong2's classloader:sun.misc.Launcher$AppClassLoader@18b4aac2
kajong3's classloader:com.example.demo.classloadertest.MyClassLoader@7699a589
引用:
1.浅谈Java 中文件的读取File、以及相对路径的问题http://www.luyixian.cn/java_show_182845.aspx
2.深入解析Java绝对路径与相对路径https://www.jianshu.com/p/982d8759edde
3.Java 类加载与自定义类加载器详解https://www.jb51.net/article/103745.htm