前言
说起Java的类加载机制,大家想必都不陌生。在我们的博文正文开始之前,我还是想多啰嗦一句。所谓的Java的类加载都遵循该顺序:Java类->字节码文件->jvm类加载器将字节码文件加载在JVM的内存中。
JVM的类加载机制
JVM 的类加载机制中有⼀个⾮常重要的角色叫做类加载器(ClassLoader),类加载器有自己的体系,JVM内置了几种类加载器,包括:引导类加载器、扩展类加载器、系统类加载器,他们之间形成父子关系,通过 Parent 属性来定义这种关系,最终可以形成树形结构。
类加载器 | 作用 |
引导启动类加载器BootstrapClassLoader | c++编写,加载java核心库 java.*,比如rt.jar中的类,构造ExtClassLoader和AppClassLoader |
扩展类加载器 ExtClassLoader | java编写,加载扩展库 JAVA_HOME/lib/ext目录下的jar中的类,如classpath中的jre ,javax.*或者java.ext.dir指定位置中的类 |
系统类加载器SystemClassLoader/AppClassLoader | 默认的类加载器,搜索环境变量 classpath 中指明的路径 |
另外:⽤户可以自定义类加载器(Java编写,用户自定义的类加载器,可加载指定路径的 class文件)
- 用户自己的类加载器,把加载请求转给父加载器,父加载器再传给其父加载器,一直加载到加载器树的顶层
- 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就交给子类
- 如果一直到最底层的类加载器都没有加载到,那么就会抛出异常ClassNotFoundException
因此,按照这个过程我么可以想到,如果同样在classpath指定的目录中和自己工作目录中存放相同的class,会优先加载classpath目录中的文件。
双亲委派机制
什么是双亲委派机制
当某个类加载器需要加载某个.class文件时,他首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
双亲委派机制的作用
- 防止重复加载同一个.class。通过委托去向上面问一遍。如果加载过了,就不再加载了,保证效率的同时也保证数据安全。
- 保证核心.class不被篡改。通过委托的方式,既是我们篡改了核心类,类加载器也不会去加载,即使加载也不是同一个.class对象了。不同的类加载器加载同一个.class也不是同一个.class对象。这样就保证了class执行的安全性。
Tomcat的类加载机制
Tomcat的类加载机制就是在JVM的类加载机制的基础上做了一些扩展。没有严格的遵循双亲委派机制,也可以说打破了双亲委派机制。比如说,我们在一个Tomcat下面部署了两个应用,两个应用下都有全路径为com.rubin.Abc的类,不同应用间的Abc类的内容是完全不一样的,如果严格遵循双亲委派机制,势必会出现类覆盖的情况出现。这种情况下,我们就需要通过自己定义类加载器加载自定义类的方式做到应用间代码的隔离。Tomcat类加载机制如下图所示:

- 引导类加载器和扩展类加载器的作用不变
- 系统类加载器正常情况下加载CLASSPATH系统变量下面的类,但是Tomcat在启动脚本中指定该加载器去加载Tomcat的启动类,即bootstrap.jar。该脚本位于CATALINA_HOME/bin包下面
- Commons类加载器加载Tomcat使用以及应用通用的一些类,位于CATALINA_HOME/lib包下面。比如servlet-api.jar
- Catalina类加载器用于加载服务器内部可见的类,这些类对于应用程序来讲是不能访问的
- Shared类加载器加载应用程序的共享类,这些类不会跟服务器有依赖
- WebApp类加载器会为每一个应用程序分配一个实例去加载对应应用的类。该类加载器用于加载应用程序包下WEB_INF/classes和WEB_INF/lib下的类
Tomcat8.5默认改变了严格的双亲委派机制:
- 首先会从Bootstrap类加载器去加载制定的类(JDK里面的类,因为这些类应用间是通用的)
- 如果未加载到,则使用WebApp类加载器去/WEB-INF/classes包下去加载
- 如果未加载到,则使用WebApp类加载器去/WEB-INF/lib包下去加载
- 如果未加载到,则依次从System、Common、Shared类加载器中去加载(该步骤是遵从双亲委派机制的)
总结
Tomcat通过对JVM类加载机制的扩展,来实现应用间类的隔离。
文章评论