Tomcat 第六篇:类加载机制 (3)

然后看下 ParallelWebappClassLoader 的 loadclass 方法是在它的父类 WebappClassLoaderBase 中实现的。

4.2.1 第一步: public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { if (log.isDebugEnabled()) log.debug("loadClass(" + name + ", " + resolve + ")"); Class<?> clazz = null; // Log access to stopped class loader checkStateForClassLoading(name); // (0) Check our previously loaded local class cache clazz = findLoadedClass0(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return clazz; } // 省略其余...

首先调用 findLoaderClass0() 方法检查 WebappClassLoader 中是否加载过此类。

protected Class<?> findLoadedClass0(String name) { String path = binaryNameToPath(name, true); ResourceEntry entry = resourceEntries.get(path); if (entry != null) { return entry.loadedClass; } return null; }

WebappClassLoader 加载过的类都存放在 resourceEntries 缓存中。

protected final Map<String, ResourceEntry> resourceEntries = new ConcurrentHashMap<>(); 4.2.2 第二步: // 省略其余... clazz = findLoadedClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return clazz; } // 省略其余...

如果第一步没有找到,则继续检查 JVM 虚拟机中是否加载过该类。调用 ClassLoader 的 findLoadedClass() 方法检查。

4.2.3 第三步: ClassLoader javaseLoader = getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try { URL url; if (securityManager != null) { PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName); url = AccessController.doPrivileged(dp); } else { url = javaseLoader.getResource(resourceName); } tryLoadingFromJavaseLoader = (url != null); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); tryLoadingFromJavaseLoader = true; } if (tryLoadingFromJavaseLoader) { try { clazz = javaseLoader.loadClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } }

如果前两步都没有找到,则使用系统类加载该类(也就是当前 JVM 的 ClassPath )。为了防止覆盖基础类实现,这里会判断 class 是不是 JVMSE 中的基础类库中类。

4.2.4 第四步: boolean delegateLoad = delegate || filter(name, true); // (1) Delegate to our parent if requested if (delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader1 " + parent); try { clazz = Class.forName(name, false, parent); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } }

先判断是否设置了 delegate 属性,设置为 true ,那么就会完全按照 JVM 的"双亲委托"机制流程加载类。

若是默认的话,是先使用 WebappClassLoader 自己处理加载类的。当然,若是委托了,使用双亲委托亦没有加载到 class 实例,那还是最后使用 WebappClassLoader 加载。

4.2.5 第五步: if (log.isDebugEnabled()) log.debug(" Searching local repositories"); try { clazz = findClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from local repository"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore }

若是没有委托,则默认会首次使用 WebappClassLoader 来加载类。通过自定义 findClass() 定义处理类加载规则。

findClass() 会去 Web-INF/classes 目录下查找类。

4.2.6 第六步: if (!delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader at end: " + parent); try { clazz = Class.forName(name, false, parent); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } }

若是 WebappClassLoader 在 /WEB-INF/classes 、 /WEB-INF/lib 下还是查找不到 class ,那么无条件强制委托给 System 、 Common 类加载器去查找该类。

4.2.7 小结

Web 应用类加载器默认的加载顺序是:

先从缓存中加载;

如果没有,则从 JVM 的 Bootstrap 类加载器加载;

如果没有,则从当前类加载器加载(按照 WEB-INF/classes 、 WEB-INF/lib 的顺序);

如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是 AppClassLoader 、 Common 、 Shared 。

参考

https://www.jianshu.com/p/69c4526b843d

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjxxw.html