TCCL通过将线程与应用类加载器绑定,使Bootstrap加载的核心类(如DriverManager)能借助当前线程的TCCL(默认AppClassLoader)加载子加载器路径下的SPI实现类,从而在不破坏双亲委派前提下实现反向类查找。
为什么父加载器需要“看到”子加载器的类Java 的双亲委派模型规定:子加载器会先委托父加载器尝试加载类,但反过来不行——启动类加载器(Bootstrap)无法访问应用类路径(如mysql-connector-java.jar)里的类。而 SPI 接口(如java.sql.Driver)在 JDK 核心库中,由 Bootstrap 加载;它的实现(如com.mysql.cj.jdbc.Driver)却在应用 classpath 下,由系统类加载器(AppClassLoader)加载。这就形成一个矛盾:接口在上层,实现在下层,可上层代码却找不到下层实现。
TCCL 是怎么打破这个限制的TCCL 不是改变类加载器结构,而是提供一个“可插拔”的加载器引用,绑定在线程上。关键点在于:ServiceLoader.load(Driver.class) 内部实际调用的是load(Driver.class, Thread.currentThread().getContextClassLoader()) JVM 启动时,主线程的 TCCL 默认设为 AppClassLoader(而非 Bootstrap),所以它天然能看见应用里的驱动类DriverManager 在初始化时主动使用 TCCL 加载服务配置(META-INF/services/java.sql.Driver)和具体实现类它不是替代双亲委派,而是补充协作TCCL 并没有废除双亲委派,而是在特定场景下“临时切换视角”。它让原本只负责“向下委托”的父级代码,获得一次“向上借力”的能力:Bootstrap 加载的 DriverManager 仍保持自身逻辑不变真正加载实现类的动作,交由当前线程的 TCCL 完成——这一步绕过了双亲委派链容器(如 Tomcat、Spring Boot)通常已自动设置好 TCCL,开发者一般无需手动干预哪些地方依赖 TCCL 才能正常工作不止 JDBC,多个 Java 标准 SPI 都依赖 TCCL 实现解耦:JCE(Java 加密扩展)
:安全算法实现由厂商提供,核心框架通过 TCCL 加载JNDI:上下文工厂(ContextFactory)需从应用 classpath 加载JAXP(XML 解析)
:SAXParserFactory、TransformerFactory 等默认使用 TCCL 查找实现日志门面(SLF4J、JUL 绑定)
:桥接器类常靠 TCCL 定位具体日志实现
