多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### Tomcat类加载器 Tomcat 的自定义类加载器`WebAppClassLoader`打破了双亲委托机制,它**首先自己尝试去加载某个类,如果找不到再代理给父类加载器**,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写`ClassLoader`的两个方法:`findClass`和`loadClass`。 ![](https://img.kancloud.cn/e0/9b/e09b5de9b96e4fb1be65fe5fce5c2aa9_569x485.png) #### findClass 方法 `org.apache.catalina.loader.WebappClassLoaderBase#findClass`;为了方便理解和阅读,我去掉了一些细节: ~~~ public Class<?> findClass(String name) throws ClassNotFoundException { ... Class<?> clazz = null; try { //1. 先在 Web 应用目录下查找类 clazz = findClassInternal(name); } catch (RuntimeException e) { throw e; } if (clazz == null) { try { //2. 如果在本地目录没有找到,交给父加载器去查找 clazz = super.findClass(name); } catch (RuntimeException e) { throw e; } //3. 如果父类也没找到,抛出 ClassNotFoundException if (clazz == null) { throw new ClassNotFoundException(name); } return clazz; } ~~~ 1. 先在 Web 应用本地目录下查找要加载的类。 2. 如果没有找到,交给父加载器去查找,它的父加载器就是上面提到的系统类加载器`AppClassLoader`。 3. 如何父加载器也没找到这个类,抛出`ClassNotFound`异常。 #### loadClass 方法 再来看 Tomcat 类加载器的`loadClass`方法的实现,同样我也去掉了一些细节: ~~~ public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> clazz = null; //1. 先在本地 cache 查找该类是否已经加载过 clazz = findLoadedClass0(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } //2. 从系统类加载器的 cache 中查找是否加载过 clazz = findLoadedClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } // 3. 尝试用 ExtClassLoader 类加载器类加载,为什么? ClassLoader javaseLoader = getJavaseClassLoader(); try { clazz = javaseLoader.loadClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } // 4. 尝试在本地目录搜索 class 并加载 try { clazz = findClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } // 5. 尝试用系统类加载器 (也就是 AppClassLoader) 来加载 try { clazz = Class.forName(name, false, parent); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } } //6. 上述过程都加载失败,抛出异常 throw new ClassNotFoundException(name); } ~~~ 主要有六个步骤: 1. 先在本地 Cache 查找该类是否已经加载过,也就是说 Tomcat 的类加载器是否已经加载过这个类。 2. 如果 Tomcat 类加载器没有加载过这个类,再看看系统类加载器是否加载过。 3. 如果都没有,就让**ExtClassLoader**去加载,这一步比较关键,目的**防止 Web 应用自己的类覆盖 JRE 的核心类**。因为 Tomcat 需要打破双亲委托机制,假如 Web 应用里自定义了一个叫 Object 的类,如果先加载这个 Object 类,就会覆盖 JRE 里面的那个 Object 类,这就是为什么 Tomcat 的类加载器会优先尝试用`ExtClassLoader`去加载,因为`ExtClassLoader`会委托给`BootstrapClassLoader`去加载,`BootstrapClassLoader`发现自己已经加载了 Object 类,直接返回给 Tomcat 的类加载器,这样 Tomcat 的类加载器就不会去加载 Web 应用下的 Object 类了,也就避免了覆盖 JRE 核心类的问题。 4. 如果`ExtClassLoader`加载器加载失败,也就是说`JRE`核心类中没有这类,那么就在本地 Web 应用目录下查找并加载。 5. 如果本地目录下没有这个类,说明不是 Web 应用自己定义的类,那么由系统类加载器去加载。这里请你注意,Web 应用是通过`Class.forName`调用交给系统类加载器的,因为`Class.forName`的默认加载器就是系统类加载器。 6. 如果上述加载过程全部失败,抛出`ClassNotFound`异常。