1. 什么是双亲委托机制
双亲委托的含义:
1) 加载的顺序固定: 引导类加载器--》扩展类加载器--》应用类加载器---》自定义类加载器
2) 当我们使用扩展类加载器加载某个类的时候,若引导类加载器已经加载的该类,就不会继续加载过程
3) 下一级类加载器的加载过程依赖上一级类加载器
4)jdk核心class只能被加载一次
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派父类加载器去完成。每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个请求时,子加载器才会尝试自己去加载。
2. tomcat的类加载机制
3. 双亲委托能否满足tomcat的业务需求
1) tomcat需要保证各个web项目的类独立加载,不同项目的相同jar可能版本不一致,需要保持依赖Jar相互独立
2) web容器依赖的类库与web项目依赖的类库隔离
3)jsp热部署的实现
默认的类加载机制需要保证class的唯一性,不能满足上述需求,也不能实现热部署。需要自定义类加载器。
4.tomcat 为什么要违背双亲委托机制
1) 双亲委托机制不能满足tomcat的业务需求
2) Webapp类加载器需要独立加载自身的class以及依赖的jar
3) 例如,webapp1依赖的spring版本为4.x,另一个依赖的spring版本为5.x. 如果使用双亲委托,那么spring的版本只能存在一个,没法满足这个需求
4) tomcat 自身依赖的jar可能和项目依赖的jar有重合的地方,比如servlet.jar, 此时优先webapp的jar加载
5. tomcat如何解决class隔离问题
每个Webapp类加载器独立去加载WEB-INF/lib与WEB-INF/classes目录下的class或ja
线程上下文加载器: 父类加载器请求子类加载器去完成类加载的动作,完成逆向双亲委托加载流程。
线程上下文类加载器(Thread Context ClassLoader):这个类加载器可以通过java.lang.Thread类的setContextClassLoader方法进行设置。如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认即使应用程序类加载器。
6. 源码实现
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized(JreCompat.isGraalAvailable() ? this : this.getClassLoadingLock(name)) { if (log.isDebugEnabled()) { log.debug("loadClass(" + name + ", " + resolve + ")"); } Class<?> clazz = null; this.checkStateForClassLoading(name); // 从本地缓存中查找 clazz = this.findLoadedClass0(name); if (clazz != null) { if (log.isDebugEnabled()) { log.debug(" Returning class from cache"); } if (resolve) { this.resolveClass(clazz); } return clazz; } // 从系统类加载器缓存中查找,已加载的类都会加入缓存 clazz = JreCompat.isGraalAvailable() ? null : this.findLoadedClass(name); if (clazz != null) { if (log.isDebugEnabled()) { log.debug(" Returning class from cache"); } if (resolve) { this.resolveClass(clazz); } return clazz; } String resourceName = this.binaryNameToPath(name, false); // 使用扩展类加载器加载,而没有使用系统类加载器,违背了双亲委托加载机制,目的是隔离 ClassLoader javaseLoader = this.getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try { URL url; if (this.securityManager != null) { PrivilegedAction<URL> dp = new WebappClassLoaderBase.PrivilegedJavaseGetResource(resourceName); url = (URL)AccessController.doPrivileged(dp); } else { url = javaseLoader.getResource(resourceName); } tryLoadingFromJavaseLoader = url != null; } catch (Throwable var13) { ExceptionUtils.handleThrowable(var13); tryLoadingFromJavaseLoader = true; } Class var10000; if (tryLoadingFromJavaseLoader) { label218: { try { clazz = javaseLoader.loadClass(name); if (clazz == null) { break label218; } if (resolve) { this.resolveClass(clazz); } var10000 = clazz; } catch (ClassNotFoundException var15) { break label218; } return var10000; } } if (this.securityManager != null) { int i = name.lastIndexOf(46); if (i >= 0) { try { this.securityManager.checkPackageAccess(name.substring(0, i)); } catch (SecurityException var12) { String error = sm.getString("webappClassLoader.restrictedPackage", new Object[]{name}); log.info(error, var12); throw new ClassNotFoundException(error, var12); } } } boolean delegateLoad = this.delegate || this.filter(name, true); if (delegateLoad) { label219: { if (log.isDebugEnabled()) { log.debug(" Delegating to parent classloader1 " + this.parent); } try { clazz = Class.forName(name, false, this.parent); if (clazz == null) { break label219; } if (log.isDebugEnabled()) { log.debug(" Loading class from parent"); } if (resolve) { this.resolveClass(clazz); } var10000 = clazz; } catch (ClassNotFoundException var17) { break label219; } return var10000; } } if (log.isDebugEnabled()) { log.debug(" Searching local repositories"); } label181: { try { clazz = this.findClass(name); if (clazz == null) { break label181; } if (log.isDebugEnabled()) { log.debug(" Loading class from local repository"); } if (resolve) { this.resolveClass(clazz); } var10000 = clazz; } catch (ClassNotFoundException var16) { break label181; } return var10000; } if (delegateLoad) { throw new ClassNotFoundException(name); } if (log.isDebugEnabled()) { log.debug(" Delegating to parent classloader at end: " + this.parent); } try { clazz = Class.forName(name, false, this.parent); if (clazz != null) { if (log.isDebugEnabled()) { log.debug(" Loading class from parent"); } if (resolve) { this.resolveClass(clazz); } var10000 = clazz; return var10000; } } catch (ClassNotFoundException var14) { ; } } throw new ClassNotFoundException(name); }