JVM 自定义类加载器
编写原则
- 在JDK1.2之前,在自定义类加载器时,总会去重写loadClass方法,从而实现自定义的类加载类,但是JDK1.2之后已不再建议用户去覆盖loadClass方法,而是建议把自定义的类加载逻辑写在findClass方法中
- 在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自已去编写findClass方法及获取字节码流的方式,使自定义类加载器编写更加简洁。
示例:
package com.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
private static final String CLASS_PATH = System.getProperty("java.class.path"); // 编译生成的.class文件的bin目录
public MyClassLoader() {
super(ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass ....");
byte[] b = loadClassFile(className);
return super.defineClass(className, b, 0, b.length);
}
private byte[] loadClassFile(String className) {
System.out.println("loadClassFile ....");
className = className.replace(".", "/");
File file = new File(CLASS_PATH + "/" + className + ".class");
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b = fis.read()) != -1) {
baos.write(b);
}
fis.close();
return baos.toByteArray();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}说明:这个示例中MyClassLoader这个类没有重写loadClass方法,因此调用loadClass方法实际调用的是父类的loadClass方法,即loadClass 实质上是使用了系统类加载器加载的,而调用 findClass 方法才是真正意义上调用自定义类加载器加载
(1)系统类加载器加载同一个类型时(即类的全限名一致),加载的Class实例只有一个,并且只加载一次。双亲委派模型
MyClassLoader myClassLoader1 = new MyClassLoader(); Class<?> clazz1 = myClassLoader1.loadClass(Sample.class.getName()); MyClassLoader myClassLoader2 = new MyClassLoader(); Class<?> clazz2 = myClassLoader2.loadClass(Sample.class.getName()); System.out.println(myClassLoader1==myClassLoader2);// false System.out.println(clazz1==clazz2);// true
下面这个实例也可以证明:
同一个类的Class对象只有一个,当该类对象被加载后,就不会再去加载该类对应的Class对象,即使又执行了加载对象的操作;
对于每一个Class对象,可以通过其getClassLoader()方法获得其类加载器的引用,所以Class对象内部有指向其类加载器的引用;
Class<?> clazz1 = ClassLoader.getSystemClassLoader().loadClass(Sample.class.getName()); Class<?> clazz2 = ClassLoader.getSystemClassLoader().loadClass(Sample.class.getName()); System.out.println(clazz1.getClassLoader());// System.out.println(clazz1==clazz2);// true
(2)用户自定义类加载器加载同一个类型时,可以加载多个Class实例。通过这种方式来破坏双亲委派模型,典型的应用如:Tomcat
MyClassLoader myClassLoader1 = new MyClassLoader(); Class<?> clazz1 = myClassLoader1.findClass(Sample.class.getName()); MyClassLoader myClassLoader2 = new MyClassLoader(); Class<?> clazz2 = myClassLoader2.findClass(Sample.class.getName()); System.out.println(myClassLoader1==myClassLoader2);// false System.out.println(clazz1==clazz2);// false
调用 findClass 表示这个类是通过自定义类加载器加载的
clazz1==clazz2 为false说明class实例在堆内存中属于不同的实例。
备注:因为上面 MyClassLoader中的findClass方法是protected类型,可能在其它包下无法访问!因此,需要再重写一个public类型的 loadClass 方法
相关推荐
bulargyzone 2020-10-26
csdnxingyuntian 2020-10-19
Locksk 2020-10-12
liufangbaishi0 2020-09-23
darlingtangli 2020-09-10
lxttiger 2020-08-18
coolrainman 2020-07-28
一支菜鸟 2020-07-27
zhouxihua0 2020-07-27
nangongyanya 2020-07-26
chenkai00 2020-07-26
voiletbin 2020-07-26
ChenRuiyz 2020-07-26
pbaabc 2020-07-23
nbfcome 2020-07-19
liufangbaishi0 2020-07-18
ChenRuiyz 2020-07-18
empireghost 2020-07-10
一支菜鸟 2020-07-06