目录
扩展类加载器(Extension ClassLoader)
应用程序加载器(Application ClassLoader)
双亲委派为什么是先往上传到bootstrap加载器,而不是从bootstrap加载器直接往下开始尝试?
双亲委派模型
类加载器:通过一个类的全限定名来获取此类的二进制字节流;
任何一个类都是由这个类本身和加载这个类的类加载器来确定类在JVM中的唯一性。
工作流程
如果一个类收到了类加载请求,首先会判断当前类是否已经被加载,若已被加载,则直接返回;若未被加载,则将类加载请求委托给父类加载器去执行,如果父类加载器还有父类加载器,则继续向上委托。如果父类加载器无法加载当前类,才会将加载任务交给子类去执行。
系统类加载器
启动类加载器(Bootstrap ClassLoader)
主要负责加载<JAVA_HOME>\lib目录中或被-Xbootclasspath指定的路径中的并且文件名是被虚拟机识别的文件。它等于是所有类加载器的爸爸。
扩展类加载器(Extension ClassLoader)
独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被java.ext.dirs系统变量所指定的路径的类库。
应用程序加载器(Application ClassLoader)
负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。
好处
- 避免类的重复加载
- 保证Java的核心API不被篡改
扩展问题
双亲委派为什么是先往上传到bootstrap加载器,而不是从bootstrap加载器直接往下开始尝试?
如果自定义类加载器存在多个,如果从bootstrap加载器往下查找,则到最底层的自定义类加载器时,就不能确定应该选择哪个自定义类加载器进行加载。
打破双亲委派模型的特例
1、JDBC
Class.forName()加载用的是调用者的Classloader,这个调用者DriverManager是在rt.jar中的,ClassLoader是启动类加载器,而com.mysql.jdbc.Driver肯定不在<JAVA_HOME>/lib下,所以肯定是无法加载mysql中的这个类的。这就是双亲委派模型的局限性了,父级加载器无法加载子级类加载器路径中的类。
只要在启动类加载器中有方法获取应用程序类加载器,然后通过它去加载就可以了。这就是所谓的线程上下文加载器。
线程上下文类加载器让父级类加载器能通过调用子级类加载器来加载类,这打破了双亲委派模型的原则。
2、Tomcat容器
一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。