1. Tomcat 类加载机制解决的问题

  1. 在一个Tomcat中部署两个应用程序时,不同的应用程序可能会依赖同一个类库的不同版本,不同要求Tomcat中只能存在一个类库的一个版本,因此要保证应用程序的类库是独立的,相互隔离。
  2. 要保证Tomcat中多个应用依赖的相同版本的类库可以共享。否则造成JVM的内存浪费,试想一下,如果10个应用依赖同一个版本的类库,如果不能共享就会加载10份一模一样的第三方类库的类,这种设计有点反人类的。
  3. Tomcat 有自己的依赖类库,基于安全考虑要保证不能和应用程序的类库混淆,让Tomcat依赖的类库和应用程序的类库隔离
  4. Tomcat容器要支持JSP的修改,也就是说修改JSP文件后不需要重新启动Tomcat就可以对JSP文件的改动生效。

2. Tomcat 如何实现类加载

141304597074685.jpg
Tomcat 启动时会创建Bootstrap引导类、System系统类、Common通用类、WebApp应用类4类加载器。

2.1. Bootstrap 引导类加载器

加载JVM启动类所需的类和标准的扩展类(位于JVM的jre/lib/ext目录下)。其实就是JVM的类加载器。

2.2. System 系统类加载器

加载CLASSPATH环境变量指定的目录,比如bootstrap.jar。通常在catalina.bat或者catalina.sh中指定,在CATALINA_HOME/bin目录下。这些类对Tomcat 内部类和web应用程序都是可见的。

2.3. Common 通用类加载器

加载Tomcat 内部类和所有web应用程序都可见的类,位于CATALINA_HOME/lib目录下,比如servlet-api.jar

2.4. WebApp 应用类加载器

Tomcat会为当前容器中的每个应用创建一个唯一的类加载器,会加载WEB-INF/lib和WEB-INF/classes目录下的class文件

3. Tomcat 类加载顺序

当Tomcat中的应用需要加载某个类时会按照下面顺序对类进行加载

  1. 使用Bootstrap 引导类加载器加载
  2. 使用System系统类加载器加载
  3. 使用应用类加载器加载WEB-INF/classes目录中的类
  4. 使用应用类加载器加载WEB-INF/lib目录中的类
  5. 使用Common通用类加载器加载CATALINA_HOME/lib目录中的类

    4. 问题扩展

通过对上面tomcat类加载机制的理解,就不难明白 为什么java文件放在Eclipse中的src文件夹下会优先jar包中的class?

这是因为Eclipse中的src文件夹中的文件java以及webContent中的JSP都会在tomcat启动时,被编译成class文件放在 WEB-INF/class 中。
而Eclipse外部引用的jar包,则相当于放在 WEB-INF/lib 中。
因此肯定是 java文件或者JSP文件编译出的class优先加载
通过这样,我们就可以简单的把java文件放置在src文件夹中,通过对该java文件的修改以及调试,便于学习拥有源码java文件、却没有打包成xxx-source的jar包。

另外呢,开发者也会因为粗心而犯下面的错误。
在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了 不同版本的jar包,此时就会导致某些情况下报加载不到类的错误。
还有如果多个应用使用同一jar包文件,当放置了多份,就可能导致 多个应用间 出现类加载不到的错误。