通过自定义类加载打破类加载双亲规则
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(name);
try{
if(clazz == null) clazz = findClass(name);
}catch (ClassNotFoundException e){
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if(clazz == null && getParent() != null){
clazz = getParent().loadClass(name);
}
if(resolve){
resolveClass(clazz);
}
return clazz;
}
其中clazz = findClass(name);必须catch,尽管loadClass也抛出了ClassNotFoundException异常,因为我们现在要实现的是,自定义类加载器加载不到(也就是找不到这个类),就让自定义加载器的父加载器(ApplicationClassLoader)去加载,如果你这里将异常抛出了,父加载器根本没有机会去加载。那么什么时候自定加载器加载不到呢,这个问题就在findClass(name)里,看你是怎么实现的findClass了。如:
/**
* 这是查找非classpath下的类(自定义目录),并未打破双亲规则
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// String className = name.substring(name.lastIndexOf("."));
// name.substring(0, name.lastIndexOf("."));
String classPath = dir + File.separator + name.replace(".", "\\") + ".class";
// File classFile = new File(dir, classPath);
File classFile = new File(classPath);
if(!classFile.exists()){
throw new ClassNotFoundException("this class "+ classPath +" is not found under [" + dir + "]");
}
byte[] classBytes = loadClassBytes(classFile);//通过class文件获取数组
if(null == classBytes || classBytes.length == 0)
throw new ClassNotFoundException("load this class is failed");
//这样还是加载不到java.*的类,因为在ClassLoader里面是写死的
Class c = AccessController.doPrivileged(new PrivilegedAction<Class>() {
@Override
public Class run() {
return defineClass(name, classBytes, 0, classBytes.length);
}
});
System.out.println("find class successfully");
return c;
}
如上,在自定义目录(非classpath)下找不到这个你这个类名指定的class文件,就会抛出ClassNotFoundException异常,所以像java.*包里的类,自定义加载器是加载不到的(就算有这个文件,也加载不到),所以这时就需要父加载器去加载了,ClassNotFoundException也由父加载器去抛出。注意从父加载器开始,还是满足双亲委托机制的。
最后说明一下,其实tomcat类加载机制并未打破类双亲委托机制,它只是没有classpath的概念,然后加载的类是从自定义目录里去加载的,所以才会去实现自定义加载器。