SPI JDNI与双亲委派模型

dalang · · 126 次点击 · · 开始浏览    
--- ### **一、双亲委派模型的核心逻辑** 双亲委派模型是 Java 类加载器(ClassLoader)的默认工作流程,其核心规则为: 1. **加载优先级**:子类加载器(如 `AppClassLoader`)在加载类时,优先委派父类加载器(如 `ExtClassLoader`)处理。 2. **避免重复加载**:父类加载器无法加载的类,才由子类加载器自行加载。 3. **安全隔离**:核心类(如 `java.lang` 包)由启动类加载器(`BootstrapClassLoader`)加载,防止恶意代码篡改。 #### **类加载器层级**: | **类加载器** | **加载路径** | **责任** | |--------------------------|---------------------------------------|-----------------------------------| | `BootstrapClassLoader` | `jre/lib` 下的核心库(如 `rt.jar`) | 加载 JDK 核心类(如 `java.*`) | | `ExtClassLoader` | `jre/lib/ext` 下的扩展库 | 加载扩展类(如 `javax.*`) | | `AppClassLoader` | 应用的 `-classpath` 或 `CLASSPATH` | 加载用户自定义类 | --- ### **二、SPI 与 JNDI 的类加载机制** #### **1. SPI 的类加载器问题** SPI 的接口(如 `java.sql.Driver`)由 `BootstrapClassLoader` 加载,但实现类(如 `com.mysql.jdbc.Driver`)通常位于应用路径下,需由 `AppClassLoader` 加载。此时双亲委派模型会导致 **父类加载器无法访问子类加载器的类**,从而无法加载 SPI 实现类。 **解决方案**: • **线程上下文类加载器(Thread Context ClassLoader, TCCL)** SPI 通过 `Thread.currentThread().getContextClassLoader()` 获取上下文类加载器(默认是 `AppClassLoader`),绕过双亲委派模型,直接加载实现类。 **示例(JDBC 驱动加载)**: ```java // DriverManager 内部使用 TCCL 加载驱动 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); ``` #### **2. JNDI 的类加载器问题** JNDI 的核心接口(如 `javax.naming.Context`)由 `BootstrapClassLoader` 加载,但其实现(如 `LDAP` 或 `RMI` 提供者)可能位于应用路径或外部库中,需由 `AppClassLoader` 加载。 **解决方案**: • **同上使用 TCCL** JNDI 通过 `Thread.currentThread().getContextClassLoader()` 加载具体实现类。 **示例(InitialContext 初始化)**: ```java // JNDI 通过 TCCL 加载 SPI 实现(如文件系统或 LDAP 提供者) Context ctx = new InitialContext(); ``` --- ### **三、类加载器调用的技术要点** #### **1. 双亲委派模型的“破坏”** • **主动绕过**:通过 `TCCL` 直接指定类加载器,使父类加载器(如 `BootstrapClassLoader`)能间接访问子类加载器(如 `AppClassLoader`)的类。 • **设计意义**:在保证核心类安全的前提下,允许动态扩展服务实现。 #### **2. TCCL 的设置** • **默认值**:线程创建时继承父线程的 TCCL,通常为 `AppClassLoader`。 • **手动设置**: ```java // 设置当前线程的上下文类加载器 Thread.currentThread().setContextClassLoader(customClassLoader); ``` #### **3. 模块化系统(Java 9+)的影响** • **模块路径(Module Path)**:Java 9 引入模块化后,SPI/JNDI 的实现类需在模块描述文件(`module-info.java`)中声明 `provides` 和 `uses`,类加载器会通过模块层(Module Layer)加载,不再完全依赖 TCCL。 --- ### **四、总结** | **场景** | **类加载器** | **双亲委派模型处理** | |------------------|----------------------------------|-----------------------------------| | SPI 接口定义 | `BootstrapClassLoader` | 父类加载器加载核心接口 | | SPI 实现类加载 | `AppClassLoader`(通过 TCCL) | 绕过双亲委派,直接加载实现类 | | JNDI 接口定义 | `BootstrapClassLoader` | 父类加载器加载核心接口 | | JNDI 实现类加载 | `AppClassLoader`(通过 TCCL) | 同上 | **结论**: SPI 和 JNDI 通过 **线程上下文类加载器(TCCL)** 绕过了双亲委派模型的限制,使父类加载器(如 `BootstrapClassLoader`)能间接加载子类加载器(如 `AppClassLoader`)路径下的服务实现类。这种机制既保留了双亲委派的安全性,又实现了服务的动态扩展能力。
126 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传