双亲委派模式的原理也十分简单,类加载器收到类加载请求,会委托给父类加载器去执行,父类加载器还存在其父类加载器,则进一步向上委托,依次递归,直到顶层类加载器,如果顶层类加载器加载到该类,就成功返回class对象,否则委托给下级类加载器去执行,依次递归(此处的父子关系并非通常所说的继承关系,而是采用组合关系来实现)。
用大白话来说就是,每个儿子都很懒,有事就丢给爹去干,直到爹说这件事我也干不了,儿子自己再想办法完成。
双亲委派模式是为了避免重复加载和核心类篡改。
特殊需求
在日常开发中,我们可能会有特殊的业务需求,可能就需要使用到自定义类加载器,该加载器的上级一定是系统类加载器。
你们想要的特殊需求
- 资源隔离
web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,因此要保证每个应用程序的类库都是独立的,相互隔离
web容器有自己依赖的类库,不能与应用程序的类库混淆,基于安全考虑,应该让容器的类库和程序的类库隔离
-
加密保护
- 公司的一些核心类库,可能会把字节码加密,这样加载类的时候就必须对字节码进行解密
-
其他来源加载类
- 字节码是放在数据库、硬盘其他路径、甚至是在云端
-
类重新加载
- JVM中类对象的唯一性:类加载器实例+完整类名
- 程序运行中,类内容发生变化,创建自定义加载器实例重新加载类,达到的热部署效果。
这里大家有个概念,理解下就够了,想深入探索就需要涉及源码分析,如果大伙有兴趣,评论区留言,阿星后续单独补一篇源码分析~
强大的父亲
有些爹的实力恐怖如斯,为了啥事都能帮后代处理好,直接破坏双亲委派模式,深受孩儿们的喜爱。
Java应用中存在着很多服务提供者接口(Service Provider Interface,SPI),这些接口允许第三方为它们提供实现,如常见的SPI有JDBC、JNDI等,这些SPI的接口属于Java核心库,一般存在rt.jar包中,由启动类加载器(Bootstrap)加载,而SPI的第三方实现代码则是作为Java应用所依赖的jar包被存放在classpath路径下。
由于SPI接口中的代码需要加载第三方实现类并调用其相关函数,但SPI的核心接口类是由启动类加载器(Bootstrap)加载的,Bootstrap加载器无法直接加载SPI的实现类。
在这种情况下,我们就需要一种特殊的类加载器来加载第三方的类库,它就是线程上下文类加载器,线程的上下文类加载器默认设置的就是系统类加载器(System)。