ClassLoader loadeClass源码:
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
1 . 首先检查,这个类是否已经被加载过了
2. 没有被加载过,如果父类加载存在,首先调用父类的加载器加载,没有父类加载使用Bootstrap ClassLoader加载
3. 如果仍然没有成功加载,调用findClass方法(这个方法可以被子类加载器重写),
从源码看出findClass方法是需要开发者自己去实现的,到这里其实就是调用实现的类加载器加载
protected Class> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
从这里看出,双亲委派模型的实现依赖于loadClass方法:
1. 如果不想不破坏双亲委派模型,只要去重写findClass方法
2. 如果想要去破坏双亲委派模型,需要去重写loadClass方法
自定义类加载器
1. 不破坏双亲委派模型,重写findClass方法
public class ProtectedClassLoader extends ClassLoader {
public ProtectedClassLoader(ClassLoader parent){
super(parent);
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] by = null;
try {
by = getByteByClassName(name);
return defineClass(name, by, 0, by.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
@Override
public InputStream getResourceAsStream(String name) {
return super.getResourceAsStream(name);
}
private byte[] getByteByClassName(String name) throws IOException {
File file = new File("D:\\MyTest.class");
InputStream is = new FileInputStream(file);
byte[] by = new byte[is.available()];
is.read(by);
is.close();
return by;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
ClassLoader ml = new ProtectedClassLoader(ClassLoader.getSystemClassLoader().getParent());
Object obj = Class.forName("com.gold.aip.test.MyTest", true, ml).newInstance();
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());
}
}
输出结果:
com.gold.aip.test.MyTest@b03be0
com.gold.aip.test.ProtectedClassLoader@14e8cee
测试这段代码的过程出现了两个问题:
1. 如果直接调用自定义类加载器的loadClass,结果还是ApplicationClassLoader,改用Class.forName指定加载器去加载
2. Myeclipse环境下,保存这个类,会自动编译,结果还是使用ApplicationClassLoader,解决方案:
* 删除classpath下编译的class文件
* 指定类加载器的父类为ExtensionClassLoader,这样父加载器无法加载,自然给子类加载加载。
* 因为指定了父类加载器,使用getResourceAsStream方法,从源码看出是调用父类加载器的这个方法,取得的字节流是空的,
改用FileInputStream直接读取
2. 破坏双亲加载模型自定义类加载器
package cn.erong.test;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
ClassLoader myloader = new ClassLoader() {
@Override
public Class> loadClass(String name)
throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
InputStream is;
try {
is =getClass().getResourceAsStream(fileName);
if(is==null) {return super.loadClass(fileName);}
byte[] by = new byte[is.available()];
is.read(by);
is.close();
return defineClass(name, by, 0, by.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
myloader.loadClass("cn.erong.test.Test").newInstance();
}
}
这段代码是有问题的,抛出异常:
java.lang.ClassNotFoundException: Object.class
不过能了解到一些东西:
* 测试类Test,继承Object,因为破坏了双亲加载模型,Object类也会使用这个加载器加载,从Classpath下找这个类,必然会出错。
* 断点测试,Object类是后加载的,因为父类永远比子类先初始化,所以可以看出父类是后加载,但是先解析,初始化