典型回答 ✅Tomcat中有哪些类加载器?
关于这个问题,网上有很多种说法,甚至我看过某国内非常知名的付费专栏中,关于这个点也并不是讲解的特别清楚。那么,这里我们先总结一下Tomcat的类加载机制,然后再来证明为啥我这么说:
Tomcat的类加载机制,在默认情况下,是先把当前要加载的类委托给BootstrapClassLoader尝试加载,为了避免JRE中的核心类被我们应用自己给覆盖(如String等),Bootstrap如果无法加载,那么就由WebAppClassLoader尝试加载,如果无法加载,那么再委托通过双亲委派的方式向上委派给Common、System等类加载进行加载,即顺序为:****Bootstrap->WebApp->System->Common
上面的是默认情况,tomcat中有一个配置**<font style="color:#F38F39;">delegate</font>**,他的默认值是false,如果设置成true了,那么他就会严格遵守双亲委派,按照****Bootstrap->System->Common->WebApp的顺序进行加载。
talk is cheap,show me the code
以下是tomcat中WebappClassLoaderBase.java中loadClass的代码,我做了一些精简,并加了一些注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //加锁,防止并发 synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) { if (log.isDebugEnabled()) { log.debug("loadClass(" + name + ", " + resolve + ")"); } Class<?> clazz = null; // ... // 检查本地缓存是否已加载该类,如果是,则直接返回缓存中的 Class 对象。 clazz = findLoadedClass0(name); if (clazz != null) { if (log.isDebugEnabled()) { log.debug(" Returning class from cache"); } if (resolve) { resolveClass(clazz); } return clazz; } // 检查另一个类加载缓存,如果是GraalVM环境,直接返回缓存中的 Class 对象。 clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name); if (clazz != null) { if (log.isDebugEnabled()) { log.debug(" Returning class from cache"); } if (resolve) { resolveClass(clazz); } return clazz; } /* * 尝试使用Bootstrap类加载器加载类,以防止Web应用程序覆盖Java SE类。如果加载成功,则返回加载的 Class 对象。 */ String resourceName = binaryNameToPath(name, false); ClassLoader javaseLoader = getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try { URL 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 } } boolean delegateLoad = delegate || filter(name, true); // 根据 delegate 属性和其他条件判断是否应该委派加载给父类加载器。 // 如果需要委派,则直接先进行委派 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 } } // 自己尝试加载 // 能走到这里,肯定是BootStrap没加载到,之后还有两种情况: // 1、如果delegate为ture的话,说明上层类加载器也没记载到。 // 2、如果delegate为false,那么就还没有进行过委派,先在这里尝试自己加载。 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 } // 如果delegate为false,说明还没有做过委派,那么委派给父类加载器加载类。 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 } } } throw new ClassNotFoundException(name); } 整个代码的过程就是:
...