理解 java 的 classloader
最近对 classloader 感兴趣, 于是研究了一下。下面是 classloader 的核心代码。
public Class<?> loadClass(String name) throws ClassNotFoundException;
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException;
protected Class<?> findClass(String name) throws ClassNotFoundException;
protected final Class<?> defineClass(byte[] b, int off, int len)
        throws ClassFormatError;
protected final void resolveClass(Class<?> c);
可以看到,只有 loadClass(String) 是 public 的成语函数,其他几个函数都是 protected ,意味着是只有子类可以调用。
例如下面的 java 普通代码
class FactoryA {
    A build() {
          return new A();
    }
}
如果 A 是第一次被访问,也就是说在此之前,如果 class A 没有被加载过,那么隐含着下面的语句在其中。
  this.getClass().getClassLoader().loadClass("full.package.name.A");
https://en.wikipedia.org/wiki/Java_Classloader 和很多其他网上文章,都讲过一下 class loader 中的几个基础 class loader ,和他们的作用。
- bootstrap class loader
- Extensions class loader
- System class loader
代理模式
大多数 class loader 会采用代理模式,因为默认的 ClassLoader 是这么做的。
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
     // First, check if the class has already been loaded
     Class<?> c = findLoadedClass(name);
     if (c == null) {
         try {
             c = parent.loadClass(name, false);
         } 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;
}
这里是简化过的代码,去掉了关于锁,bootstrap 和统计的部分。可以看到,默认的实现是这样的。
- 首先判断是否已经加载过。如果加载过,用以前的,不会重复加载。
- 如果没有,那么代理给 parent来加载,bootstrap class loader 没有 parent ,我们先忽略 bootstrap 相关的处理代码。
- 如果 parent没有加载成功,那么调用findClass来处理。
- 调用 resolveClass来连接(linking) 相关的依赖的类。这个也可能引入继续加载其他的类。
默认的 findClass 直接抛出 ClassNotFoundException
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}
这意味着,如果我们自己写一个自定义的 class loader 类,那么我们最好重载 findClass 这个成员函数。
如果我们定义一个 findClass 那么我们如何返回一个 Class<?> 对象呢? 答案就是,调用 defineClass。
首先,我们需要找到 class 的字节码,表示称为 byte[] ,然后传递给  defineClass 返回一个可用的 Class<?> 对象。
这里还有一个问题,this.getClass().getClassLoader() 返回的是哪一个 class loader 呢?
答案就是,如果某一个 class loader 调用了 defineClass 返回了某一个 Class<?> 对象,那么这个 Class<?>#getClassLoader() 函数,就会返回这个 class loader 。
这个规则看起来很简单,但是我们需要分析一下,可能的后果。
String 是一个很常用的类,是 bootstap class loader 定义的,换句话说,是 bootstrap class loader 调用了 defineClass 返回的 Class<String> 。那么,在 String 这个类里面,依赖的其他类,例如  Object ,就会直接调用 bootstrap class loader 来加载。不会使用 extension class loader 或者 system class loader 。
我们都想想这个规则,就会发现,如果子类的行为重新定义了搜索顺序,那么就很危险了,容易因为使用者的混乱。
例如 tomcat 的 war class loader ,他重新定义了 loadClass ,先看看自己能否找到 class ,如果找不到,然后再代理给 parent ,即 ear class loader 去搜索。