Tomcat 架构原理解析到架构设计借鉴 (7)

这些类加载器的工作原理是一样的,区别是它们的加载路径不同,也就是说 findClass这个方法查找的路径不同。双亲委托机制是为了保证一个 Java 类在 JVM 中是唯一的,假如你不小心写了一个与 JRE 核心类同名的类,比如 Object类,双亲委托机制能保证加载的是 JRE里的那个 Object类,而不是你写的 Object类。这是因为 AppClassLoader在加载你的 Object 类时,会委托给 ExtClassLoader去加载,而 ExtClassLoader又会委托给 BootstrapClassLoader,BootstrapClassLoader发现自己已经加载过了 Object类,会直接返回,不会去加载你写的 Object类。我们最多只能 获取到 ExtClassLoader这里注意下。

Tomcat 热加载

Tomcat 本质是通过一个后台线程做周期性的任务,定期检测类文件的变化,如果有变化就重新加载类。我们来看 ContainerBackgroundProcessor具体是如何实现的。

protected class ContainerBackgroundProcessor implements Runnable { @Override public void run() { // 请注意这里传入的参数是 " 宿主类 " 的实例 processChildren(ContainerBase.this); } protected void processChildren(Container container) { try { //1. 调用当前容器的 backgroundProcess 方法。 container.backgroundProcess(); //2. 遍历所有的子容器,递归调用 processChildren, // 这样当前容器的子孙都会被处理 Container[] children = container.findChildren(); for (int i = 0; i < children.length; i++) { // 这里请你注意,容器基类有个变量叫做 backgroundProcessorDelay,如果大于 0,表明子容器有自己的后台线程,无需父容器来调用它的 processChildren 方法。 if (children[i].getBackgroundProcessorDelay() <= 0) { processChildren(children[i]); } } } catch (Throwable t) { ... }

Tomcat 的热加载就是在 Context 容器实现,主要是调用了 Context 容器的 reload 方法。抛开细节从宏观上看主要完成以下任务:

停止和销毁 Context 容器及其所有子容器,子容器其实就是 Wrapper,也就是说 Wrapper 里面 Servlet 实例也被销毁了。

停止和销毁 Context 容器关联的 Listener 和 Filter。

停止和销毁 Context 下的 Pipeline 和各种 Valve。

停止和销毁 Context 的类加载器,以及类加载器加载的类文件资源。

启动 Context 容器,在这个过程中会重新创建前面四步被销毁的资源。

在这个过程中,类加载器发挥着关键作用。一个 Context 容器对应一个类加载器,类加载器在销毁的过程中会把它加载的所有类也全部销毁。Context 容器在启动过程中,会创建一个新的类加载器来加载新的类文件。

Tomcat 的类加载器

Tomcat 的自定义类加载器 WebAppClassLoader打破了双亲委托机制,它首先自己尝试去加载某个类,如果找不到再代理给父类加载器,其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader的两个方法:findClass和 loadClass。

findClass 方法

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

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