文章目录
- 引入
- Tomcat类加载器是怎么样的?
引入
之前文章Java架构直通车——以JDBC为例谈双亲委派模型的破坏谈到了对于双亲委派模型的破坏,原因是启动类、拓展类加载器并不能加载jdbc Driver,而只有应用类加载器可以;所以需要在启动类加载器中获取到应用类加载器并加载这个类。
而在Tomcat中,也是由于同样的原因:
在Java核心类里面有SPI(Service Provider Interface),它由Sun编写规范,第三方来负责实现。SPI需要用到第三方实现类。如果使用双亲委派模型,那么第三方实现类也需要放在Java核心类里面才可以,不然的话第三方实现类将不能被加载使用。
简单来说就是由于SPI,由第三方来负责实现的类是无法放在Java核心类里面的,只能由第三方实现类的类加载器做加载。
这时ContextClassLoader(上下文类加载器)就来解围了。在java.lang.Thread里面有两个方法,get/set上下文类加载器:
public void setContextClassLoader(ClassLoader cl)
public ClassLoader getContextClassLoader()
我们可以通过在SPI类里面调用getContextClassLoader来获取第三方实现类的类加载器。由第三方实现类通过调用setContextClassLoader来传入自己实现的类加载器, 这样就变相地解决了双亲委派模式遇到的问题。
Tomcat类加载器是怎么样的?
Java默认的类加载机制是通过双亲委派模型来实现的,而Tomcat实现的方式又和双亲委派模型有所区别。
原因在于一个Tomcat容器允许同时运行多个Web程序,每个Web程序依赖的类又必须是相互隔离的。因此,如果Tomcat使用双亲委派模式来加载类的话,将导致Web程序依赖的类变为共享的。
结合经典的类加载机制,我们完整的看下Tomcat类加载图
我们在这张图中看到很多类加载器,除了Jdk自带的类加载器,我们尤其关心Tomcat自身持有的类加载器。仔细一点我们很容易发现:Catalina类加载器和Shared类加载器,他们并不是父子关系,而是兄弟关系。为啥这样设计,我们得分析一下每个类加载器的用途,才能知晓。
- Common类加载器,负责加载Tomcat和Web应用都复用的类
- Catalina类加载器,负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见
- Shared类加载器,负责加载Tomcat下所有的Web应用程序都复用的类,而这些被加载的类在Tomcat中将不可见
- WebApp类加载器,负责加载具体的某个Web应用程序所使用到的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见
- Jsp类加载器,每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔
Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托。