Spring的加载问题JVM默认情况下,若一个类由类加载器A加载,则该类的依赖类也由相同的类加载器加载 。
比如Spring作为一个Bean工厂,它需要创建业务类的实例,并且在创建业务类实例之前需要加载这些类 。Spring是通过调用Class.forName来加载业务类的,我们来看一下forName的源码:
public static Class<?> forName(String className) {Class<?> caller = Reflection.getCallerClass();return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}会使用调用者,即Spring的加载器去加载业务类 。
Web应用之间共享的jar可交给SharedClassLoader加载,以避免重复加载 。Spring作为共享的三方jar,本身由SharedClassLoader加载,Spring又要去加载业务类,按照前面那条规则,加载Spring的类加载器也会用来加载业务类,但是业务类在Web应用目录下,不在SharedClassLoader的加载路径下,这该怎么办呢?
线程上下文加载器于是有了线程上下文加载器,一种类加载器传递机制 。因为该类加载器保存在线程私有数据里,只要是同一个线程,一旦设置了线程上下文加载器,在线程后续执行过程中就能把这个类加载器取出来用 。因此Tomcat为每个Web应用创建一个WebAppClassLoader类加载器,并在启动Web应用的线程里设置线程上下文加载器,这样Spring在启动时就将线程上下文加载器取出来,用来加载Bean 。Spring取线程上下文加载的代码如下:
cl = Thread.currentThread().getContextClassLoader();在StandardContext的启动方法,会将当前线程的上下文加载器设置为WebAppClassLoader 。
启动方法结束时,会恢复线程的上下文加载器:
Thread.currentThread().setContextClassLoader(originalClassLoader);
这是为什么呢?线程上下文加载器其实是线程的一个私有数据,跟线程绑定,这个线程完成启动Context组件后,会被回收到线程池,之后被用来做其他事情,为了不影响其他事情,需恢复之前的线程上下文加载器 。
优先加载web应用的类,当加载完了再改回原来的 。
线程上下文的加载器就是指定子类加载器来加载具体的某个桥接类,比如JDBC的Driver的加载 。
总结Tomcat的Context组件为每个Web应用创建一个WebAppClassLoader类加载器,由于不同类加载器实例加载的类是互相隔离的,因此达到了隔离Web应用的目的,同时通过CommonClassLoader等父加载器来共享第三方JAR包 。而共享的第三方JAR包怎么加载特定Web应用的类呢?可以通过设置线程上下文加载器来解决 。
多个应用共享的Java类文件和JAR包,分别放在Web容器指定的共享目录:
CommonClassLoader
对应
<Tomcat>/common/*
CatalinaClassLoader
对应
<Tomcat >/server/*
SharedClassLoader
对应
<Tomcat >/shared/*
WebAppClassloader
对应
<Tomcat >/webapps/<app>/WEB-INF/*
可以在Tomcat conf目录下的Catalina.properties文件里配置各种类加载器的加载路径 。
当出现ClassNotFound错误时,应该检查你的类加载器是否正确 。
线程上下文加载器不仅仅可以用在Tomcat和Spring类加载的场景里,核心框架类需要加载具体实现类时都可以用到它,比如我们熟悉的JDBC就是通过上下文类加载器来加载不同的数据库驱动的 。
【Tomcat打破双亲委派机制实现隔离Web应用的方法】到此这篇关于Tomcat打破双亲委派机制实现隔离Web应用的方法的文章就介绍到这了,更多相关Tomcat 隔离Web应用内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
推荐阅读
- 使用jconsole监听Tomcat远程服务的配置方法
- 云转型谈何容易?打破转型阵痛,汇量科技加码云原生
- Tomcat的Server Options选项详解大全
- nginx和tomcat区别
- 汽车刷ecu有哪些影响
- 梦见跟人打架头被打破了
- 原神风墙怎么打破
- 奇怪的冰怎么打破
- 原神雪山蓝色远古石头怎么打破
- 张仪怎么打破六国合纵的