tomcat源码阅读(三)——ClassLoader背景知识
前几天想了一下,最近主要学习linux和httpd,所以tomcat源码阅读先放一放,可能到9月份左右再继续。不过先把已经写好的几篇陆续贴上来
tomcat用到很多ClassLoader相关的代码,如果缺乏这方面的背景知识,阅读源码会遇到很多障碍,所以本文首先总结一下这方面的内容,和tomcat源码的关系不大
1 标准的ClassLoader体系
1.1 bootstrap
bootstrap classloader是由JVM启动的,用于加载%JAVA_HOME%/jre/lib/下的JAVA平台自身的类(比如rt.jar中的类等)。这个classloader位于JAVA类加载器链的顶端,是用C/C++开发的,而且JAVA应用中没有任何途径可以获取到这个实例,它是JDK实现的一部分
1.2 extension
entension classloader用于加载%JAVA_HOME%/jre/lib/ext/下的类,它的实现类是sun.misc.Launcher$ExtClassLoader,是一个内部类
基本上,我们开发的JAVA应用都不太需要关注这个类
1.3 system
system classloader是jvm启动时,根据classpath参数创建的类加载器(如果没有显式指定classpath,则以当前目录作为classpath)。在普通的JAVA应用中,它是最重要的类加载器,因为我们写的所有类,通常都是由它加载的。这个类加载器的实现类是sun.misc.Launch$AppClassLoader
用ClassLoader.getSystemClassLoader(),可以得到这个类加载器
1.4 custom
一般情况下,对于普通的JAVA应用,ClassLoader体系就到system为止了。平时编程时,甚至都不会感受到classloader的存在
但是对于其他一些应用,比如web server,插件加载器等,就必须和ClassLoader打交道了。这时候默认的类加载器不能满足需求了(类隔离、运行时加载等需求),需要自定义类加载器,并挂载到ClassLoader链中(默认会挂载到system classloader下面)
2 双亲委派模型
从上面的图可以看到,classloader链,是一个自上而下的树形结构。一般来说,java中的类加载,是遵循双亲委派模型的,即:
当一个classloader要加载一个类时,首先会委托给它的parent classloader来加载,如果parent找不到,才会自己加载。如果最后也找不到,则会抛出熟悉的ClassNotFoundException
这个模型,是在最基础的抽象类ClassLoader里确定的:
protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) { 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. c = findClass(name); }}if (resolve) { resolveClass(c);}return c; }@Deprecatedpublic class StandardClassLoader extends URLClassLoader implements StandardClassLoaderMBean { public StandardClassLoader(URL repositories[]) { super(repositories); } public StandardClassLoader(URL repositories[], ClassLoader parent) { super(repositories, parent); }}protected ClassLoader(ClassLoader parent) { this(checkCreateClassLoader(), parent); }protected ClassLoader() { this(checkCreateClassLoader(), getSystemClassLoader()); }Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.newInstance();this.getClass().getClassLoader();
public class Test {public void tryForName() {System.out.println("current classloader: "+ this.getClass().getClassLoader());try {Class.forName("net.kyfxbl.test.cl.Target"); System.out.println("load class success");} catch (ClassNotFoundException e) {e.printStackTrace();}}}
public static void main(String[] args) {Test t = new Test();t.tryForName();}public static void main(String[] args) throws Exception {ClassLoader cl = createClassLoader();Class<?> startupClass = cl.loadClass("net.kyfxbl.test.cl.Test");Object startupInstance = startupClass.newInstance();String methodName = "tryForName";Class<?>[] paramTypes = new Class[0];Object[] paramValues = new Object[0];Method method = startupInstance.getClass().getMethod(methodName,paramTypes);method.invoke(startupInstance, paramValues);}private static ClassLoader createClassLoader() throws MalformedURLException {String filePath = "c://hehe.jar";File file = new File(filePath);URL url = file.toURI().toURL();URL[] urls = new URL[] { url };ClassLoader myClassLoader = new URLClassLoader(urls);return myClassLoader;}

public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
public class Test {public void tryForName() {System.out.println("current classloader: "+ getClass().getClassLoader());System.out.println("thread context classloader: "+ Thread.currentThread().getContextClassLoader());try {Class.forName("net.kyfxbl.test.cl.Target");System.out.println("load class success");} catch (Exception exc) {exc.printStackTrace();}}}public static void main(String[] args) throws Exception {ClassLoader cl = createClassLoader();Class<?> startupClass = cl.loadClass("net.kyfxbl.test.cl.Test");final Object startupInstance = startupClass.newInstance();new Thread(new Runnable() {@Overridepublic void run() {String methodName = "tryForName";Class<?>[] paramTypes = new Class[0];Object[] paramValues = new Object[0];try {Method method = startupInstance.getClass().getMethod(methodName, paramTypes);method.invoke(startupInstance, paramValues);} catch (Exception exc) {exc.printStackTrace();}}}).start();}