想象一下:一家大公司的采购系统
把 Java 虚拟机 (JVM) 想象成一家等级森严的大公司。
把 类 (Class) 想象成公司需要的各种物料(比如:纸、笔、电脑、专用的螺丝钉)。
把 类加载器 (ClassLoader) 想象成公司里负责采购物料的不同级别的采购员。
这家公司有三级采购员:
CEO 助理 (顶层 - 启动类加载器 Bootstrap CL)
职责:只负责采购公司生存最核心、最机密的物料,比如公司的公章、空白支票、CEO的专用钢笔。
物料来源:他的采购范围仅限于一个“CEO的私人保险柜” (jre/lib目录,比如 rt.jar)。
特点:地位最高,但他不认识、也不屑于去了解公司其他部门的普通物料。
总务部经理 (中间层 - 扩展类加载器 Extension CL)
职责:负责采购公司通用的、可扩展的物料,比如标准化的键盘、鼠标。
物料来源:他的采购范围是“公司中央仓库” (jre/lib/ext目录)。
你的部门助理 (基层 - 应用程序类加载器 Application CL)
职责:负责采购你这个项目组需要的特定物料。
物料来源:他的采购范围是你项目组的“合作供应商列表” (就是你的项目 classpath,包含了你引入的各种 jar 包)。
“双亲委派”:公司的标准采购流程
公司的采购流程非常严格,这个流程就叫“双亲委派”。
规则: 无论谁需要物料,申请总是先往上报,直到顶层。只有当上级说“我这没有”时,下级才能自己去采购。
举个例子:你的代码需要一张“A4打印纸” (java.lang.String)
你的申请: 你告诉你的部门助理:“我需要一张‘A4打印纸’”。
部门助理上报: 你的助理不会马上自己去买。他会先问他的上级——总务部经理:“我需要一张‘A4打印纸’,您那有吗?”
总务部经理继续上报: 总务部经理也不会自己去仓库找。他会先问他的上级——CEO助理:“我需要一张‘A4打印纸’,您那有吗?”
顶层处理: CEO助理收到了申请。他打开“CEO的私人保险柜”,发现里面有标准化的“A4打印纸”(因为 String 是 Java 最核心的类)。他直接拿出这张纸,然后把纸层层递回给你。
流程结束: 采购成功!整个过程中,你的部门助理和总务部经理根本没动手。
这个流程的好处?
保证了公司使用的核心物料绝对统一。无论谁申请“A4打印纸”,最终拿到的都是CEO助理从保险柜里拿出的那一种,避免了有人买到假冒伪劣产品,保证了公司的安全。
“SPI 打破规则”:当 CEO 需要一个“民间”的零件
现在,出现了一个特殊情况。
场景:JDBC
CEO (JDK核心代码) 正在读一本《标准设备组装手册》(java.sql.Driver 接口)。
手册上说:“要组装这个设备,你需要一个‘MySQL牌的特制螺丝钉’ (com.mysql.cj.jdbc.Driver 实现类)”。
于是,CEO命令他的CEO助理:“去给我找一个‘MySQL牌的特制螺丝钉’来。”
悖论出现了!
CEO助理的困境: 他收到了命令,但是按照规定,他只能在他自己的“私人保险柜”里找。他找了一遍,发现里面只有公章和钢笔,根本没有这种来自“民间”的螺丝钉。
规则的束缚: 按照“双亲委派”的规定,申请只能向上汇报。CEO助理是最高层,他没有上级可以问了。同时,他也绝不允许“向下”去问他的下属(总务部经理或你的部门助理)。
系统卡死了! CEO助理找不到零件,又不能问下属,任务无法完成。
“线程上下文类加载器”:CEO 的“特派专员”
为了解决这个死结,公司引入了一个“B计划”:“特派专员”(线程上下文类加载器)。
他是谁? 这个“特派专员”其实就是你的“部门助理”的一个分身。当你的项目启动时,CEO办公室就拿到了这个“特派专员”的联系方式。
新的工作流程:
CEO助理再次收到命令:“去给我找一个‘MySQL牌的特制螺丝钉’。”
他一看,认出这是一个定义在《标准设备组装手册》(SPI) 里的特殊零件。
他不再去翻自己的保险柜,而是拿出一个“紧急电话”,直接打给“特派专员”。
“特派专员”(也就是你的部门助理)接到了来自顶层的直接命令!
他非常清楚这个“MySQL牌的特制螺丝钉”在哪个“合作供应商”那里(因为 mysql-connector-java.jar 就在你的项目 classpath 里)。
他立刻采购到了这个螺丝钉,通过“特派专员”的渠道,直接递交给了CEO助理。
CEO助理再转交给CEO。任务完成!