适用版本:Tomcat 7.0.99 & Tomcat 8.x (理论上不限于7.x & 8.x)

结构

  1. Bootstrap
  2. |
  3. System
  4. |
  5. Common
  6. / \
  7. Webapp1 Webapp2 ...
  • Bootstrap: 包含了JVM提供的运行时环境 + $JAVA_HOME/jre/lib/ext
  • System:等价于AppClassLoader ,从classpath中初始化而来,写一个main方法,他的classloader就是system/appClassloader.
  • Common: 加载tomcat的$CATALINA_BASE/lib 和 $CATALINA_HOME/lib 下的资源
  • Webapp: 业务用到的classloader,加载WEB-INF/lib 和 WEB-INF/class下边的资源

加载过程

假设Tomcat的启动过程是 ./startup.sh .
**

  • 启动Bootstrap.main方法,只是的classloader是appClassLoader(SystemClassLoader), 加载的jar包仅仅有 bootstrap.jar、tomcat-juli.jar等少数jar包
  • 新建Common加载Catalina等数据,此时他的jar包是lib下的jar包,包括但不限于jsp-api.jar和servlet-api.jar等等
  • 随着Tomcat的启动,在StandardContext中新建WebappClassLoader并加载我们的业务代码(加载WEB-INF/lib 和 WEB-INF/class下的资源),其中class的优先级比lib的要高

SPI (Service Provider Interface)和上下文类加载器

SPI 全称为(Service Provider Interface) : 服务提供接口,主要由厂商提供,非常典型的就是JDBC,在我刚接触的时候,单独使用JDBC,是要使用Class.forName() 进行初始化的,后来由于SPI的普及和应用,forName渐渐的不用了。用于服务发现.

ClassA中出现的其他class,也应该由加载ClassA的classLoader来进行加载. 用JDBC为例,DriverManager是jdk自带的,那么加载他的classloader应该是boot,而在DriverManager中由SPI机制找到的服务厂商提供的class也应该DriverManager的classloader进行加载,而厂商提供的class是不应该由boot加载的,这个时候就是ContextClassloader的用处了. 这种使用模式在APM中使用较多.

总结

理性看到classloader,其实并没有那么负载,主要是要理解他的依赖和传递的加载关系。