欢迎光临
我们一直在努力

双亲委派模型破坏: Tomcat和JDBC驱动是如何打破双亲委派机制的?

双亲委派模型破坏: Tomcat和JDBC驱动是如何打破双亲委派机制的?

引言: 一个"必须被打破"的设计原则

双亲委派模型是Java类加载机制的核心原则,它保证了Java类型体系的稳定与安全。但在实际开发中,你会发现Tomcat、JDBC、OSGi等框架都在"破坏"这个模型。

这不是设计缺陷,而是工程实践对理论模型的合理突破。当应用需要隔离性(多个Web应用各自加载不同的库版本)和反向可见性(SPI机制下父加载器需要访问子加载器的类)时,双亲委派模型就显得力不从心。

本节将深入剖析双亲委派模型的局限性,并通过Tomcat和JDBC两个经典案例,揭示框架设计者是如何"优雅地破坏"这个模型的。

一、经典双亲委派模型的回顾

1.1 三层类加载器架构

#publish-mermaid-1784692885017-0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1784692885017-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885017-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885017-0 .error-icon{fill:#552222;}#publish-mermaid-1784692885017-0 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1784692885017-0 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1784692885017-0 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1784692885017-0 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1784692885017-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1784692885017-0 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1784692885017-0 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1784692885017-0 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1784692885017-0 .marker.cross{stroke:#333333;}#publish-mermaid-1784692885017-0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1784692885017-0 p{margin:0;}#publish-mermaid-1784692885017-0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1784692885017-0 .cluster-label text{fill:#333;}#publish-mermaid-1784692885017-0 .cluster-label span{color:#333;}#publish-mermaid-1784692885017-0 .cluster-label span p{background-color:transparent;}#publish-mermaid-1784692885017-0 .label text,#publish-mermaid-1784692885017-0 span{fill:#333;color:#333;}#publish-mermaid-1784692885017-0 .node rect,#publish-mermaid-1784692885017-0 .node circle,#publish-mermaid-1784692885017-0 .node ellipse,#publish-mermaid-1784692885017-0 .node polygon,#publish-mermaid-1784692885017-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885017-0 .rough-node .label text,#publish-mermaid-1784692885017-0 .node .label text,#publish-mermaid-1784692885017-0 .image-shape .label,#publish-mermaid-1784692885017-0 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1784692885017-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1784692885017-0 .rough-node .label,#publish-mermaid-1784692885017-0 .node .label,#publish-mermaid-1784692885017-0 .image-shape .label,#publish-mermaid-1784692885017-0 .icon-shape .label{text-align:center;}#publish-mermaid-1784692885017-0 .node.clickable{cursor:pointer;}#publish-mermaid-1784692885017-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1784692885017-0 .arrowheadPath{fill:#333333;}#publish-mermaid-1784692885017-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1784692885017-0 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1784692885017-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885017-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885017-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885017-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1784692885017-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1784692885017-0 .cluster text{fill:#333;}#publish-mermaid-1784692885017-0 .cluster span{color:#333;}#publish-mermaid-1784692885017-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1784692885017-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1784692885017-0 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1784692885017-0 .icon-shape,#publish-mermaid-1784692885017-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885017-0 .icon-shape p,#publish-mermaid-1784692885017-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1784692885017-0 .icon-shape .label rect,#publish-mermaid-1784692885017-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885017-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1784692885017-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1784692885017-0 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node rect,#publish-mermaid-1784692885017-0 [data-look=\”neo\”].cluster rect,#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885017-0 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885017-0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

委派方向

双亲委派模型1. 先委托父加载器2. 再委托父加载器3. 父加载器找不到4. 父加载器找不到5. 自己尝试加载

Bootstrap ClassLoader加载: rt.jar, 核心类

Extension ClassLoader加载: jre/lib/ext/*.jar

Application ClassLoader加载: classpath下的类

1.2 核心代码实现

/**
* ClassLoader.loadClass() 的核心逻辑
* 这是双亲委派模型的代码体现
*/
public class ParentDelegationModel {

// JDK中ClassLoader.loadClass()的简化逻辑
// protected Class<?> loadClass(String name, boolean resolve) {
// synchronized (getClassLoadingLock(name)) {
// // 第一步: 检查是否已加载
// Class<?> c = findLoadedClass(name);
//
// if (c == null) {
// try {
// // 第二步: 委托父加载器加载
// if (parent != null) {
// c = parent.loadClass(name, false);
// } else {
// // 父加载器为空,使用Bootstrap ClassLoader
// c = findBootstrapClassOrNull(name);
// }
// } catch (ClassNotFoundException e) {
// // 父加载器找不到,继续
// }
//
// if (c == null) {
// // 第三步: 自己尝试加载
// c = findClass(name);
// }
// }
//
// if (resolve) {
// resolveClass(c);
// }
// return c;
// }
// }

public static void main(String[] args) {
System.out.println("=== 双亲委派模型核心规则 ===\\n");
System.out.println("1. 子加载器先委托父加载器");
System.out.println("2. 父加载器找不到,子加载器才自己加载");
System.out.println("3. 保证核心类不被篡改(如java.lang.String)");
System.out.println("4. 保证同一个类在同一个加载器体系下是唯一的");
}
}

1.3 双亲委派模型的局限性

public class DelegationLimitations {

public static void main(String[] args) {
System.out.println("=== 双亲委派模型的局限 ===\\n");

System.out.println("局限1: 无法实现应用隔离");
System.out.println(" 场景: 同一个Tomcat部署两个Web应用");
System.out.println(" 问题: App-A需要spring-5.0.jar, App-B需要spring-6.0.jar");
System.out.println(" 冲突: 双亲委派下只能加载一个版本\\n");

System.out.println("局限2: 无法实现反向可见性(SPI困境)");
System.out.println(" 场景: JDBC驱动加载");
System.out.println(" 问题: java.sql.DriverManager在Bootstrap层");
System.out.println(" 问题: MySQL驱动在Application层");
System.out.println(" 冲突: 父加载器无法看到子加载器的类\\n");

System.out.println("局限3: 无法动态卸载类");
System.out.println(" 场景: Web应用热部署");
System.out.println(" 问题: 修改JSP后需要重启才能生效");
System.out.println(" 冲突: 双亲委派下类加载后无法卸载");
}
}

二、Tomcat如何打破双亲委派: 实现Web应用隔离

2.1 Tomcat的类加载器架构

#publish-mermaid-1784692885077-1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1784692885077-1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885077-1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885077-1 .error-icon{fill:#552222;}#publish-mermaid-1784692885077-1 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1784692885077-1 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1784692885077-1 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1784692885077-1 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1784692885077-1 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1784692885077-1 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1784692885077-1 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1784692885077-1 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1784692885077-1 .marker.cross{stroke:#333333;}#publish-mermaid-1784692885077-1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1784692885077-1 p{margin:0;}#publish-mermaid-1784692885077-1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1784692885077-1 .cluster-label text{fill:#333;}#publish-mermaid-1784692885077-1 .cluster-label span{color:#333;}#publish-mermaid-1784692885077-1 .cluster-label span p{background-color:transparent;}#publish-mermaid-1784692885077-1 .label text,#publish-mermaid-1784692885077-1 span{fill:#333;color:#333;}#publish-mermaid-1784692885077-1 .node rect,#publish-mermaid-1784692885077-1 .node circle,#publish-mermaid-1784692885077-1 .node ellipse,#publish-mermaid-1784692885077-1 .node polygon,#publish-mermaid-1784692885077-1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885077-1 .rough-node .label text,#publish-mermaid-1784692885077-1 .node .label text,#publish-mermaid-1784692885077-1 .image-shape .label,#publish-mermaid-1784692885077-1 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1784692885077-1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1784692885077-1 .rough-node .label,#publish-mermaid-1784692885077-1 .node .label,#publish-mermaid-1784692885077-1 .image-shape .label,#publish-mermaid-1784692885077-1 .icon-shape .label{text-align:center;}#publish-mermaid-1784692885077-1 .node.clickable{cursor:pointer;}#publish-mermaid-1784692885077-1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1784692885077-1 .arrowheadPath{fill:#333333;}#publish-mermaid-1784692885077-1 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1784692885077-1 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1784692885077-1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885077-1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885077-1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885077-1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1784692885077-1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1784692885077-1 .cluster text{fill:#333;}#publish-mermaid-1784692885077-1 .cluster span{color:#333;}#publish-mermaid-1784692885077-1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1784692885077-1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1784692885077-1 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1784692885077-1 .icon-shape,#publish-mermaid-1784692885077-1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885077-1 .icon-shape p,#publish-mermaid-1784692885077-1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1784692885077-1 .icon-shape .label rect,#publish-mermaid-1784692885077-1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885077-1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1784692885077-1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1784692885077-1 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node rect,#publish-mermaid-1784692885077-1 [data-look=\”neo\”].cluster rect,#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885077-1 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885077-1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}加载优先级(与双亲委派相反!)

1. Bootstrap

2. System

3. Common

4. Webapp自己

5. Webapp父类

Tomcat类加载器架构

Bootstrap ClassLoaderJVM核心类

Common ClassLoaderTomcat公共类

WebappClassLoader-A应用A的类

WebappClassLoader-B应用B的类

WebappClassLoader-C应用C的类

JspClassLoader-AJSP编译类

JspClassLoader-BJSP编译类

System ClassLoaderTomcat启动类

2.2 Tomcat打破双亲委派的代码原理

/**
* Tomcat的WebappClassLoader重写了loadClass方法
* 核心: 优先从当前Webapp的WEB-INF/classes和WEB-INF/lib加载
* 只有在找不到时才委托给父加载器
*/
public class TomcatClassLoaderPrinciple {

// 这是Tomcat WebappClassLoader的简化逻辑

// @Override
// public Class<?> loadClass(String name, boolean resolve) {
// synchronized (getClassLoadingLock(name)) {
// // 第一步: 检查是否已加载
// Class<?> clazz = findLoadedClass(name);
//
// if (clazz == null) {
// try {
// // 第二步: 尝试委托给Bootstrap(只对JVM核心类)
// clazz = bootstrapClassLoader.loadClass(name);
// } catch (ClassNotFoundException e) {
// // Bootstrap没有,继续
// }
//
// if (clazz == null) {
// // 第三步: 尝试自己加载(先于父加载器!)
// // 从WEB-INF/classes和WEB-INF/lib加载
// clazz = findClass(name);
//
// if (clazz == null) {
// // 第四步: 自己找不到,委托给父加载器
// clazz = parent.loadClass(name);
// }
// }
// }
//
// return clazz;
// }
// }

public static void main(String[] args) {
System.out.println("=== Tomcat打破双亲委派的策略 ===\\n");

System.out.println("策略: 优先自己加载,找不到才委托父加载器\\n");

System.out.println("加载顺序:");
System.out.println("1. 检查是否已加载(缓存)");
System.out.println("2. 委托给Bootstrap(只针对JVM核心类)");
System.out.println("3. 尝试自己加载(WEB-INF/classes和WEB-INF/lib)");
System.out.println("4. 自己找不到,委托给父加载器(System/Common)\\n");

System.out.println("为什么这样设计?");
System.out.println("- 每个Web应用使用独立的WebappClassLoader");
System.out.println("- 优先加载应用自己的类库");
System.out.println("- 实现应用间类库版本隔离");
System.out.println("- spring-5.0.jar和spring-6.0.jar可以共存");
}
}

2.3 Tomcat的类加载隔离演示

/**
* Tomcat类加载隔离演示
*
* 每个Web应用拥有独立的WebappClassLoader
* 同一个类在不同WebappClassLoader中是不同的
*/
public class TomcatIsolationDemo {

// 模拟两个Web应用的类加载器
static class WebappClassLoader extends ClassLoader {
private final String appName;
private final String libPath;

public WebappClassLoader(String appName, String libPath, ClassLoader parent) {
super(parent);
this.appName = appName;
this.libPath = libPath;
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// Tomcat模式: 优先自己加载
synchronized (getClassLoadingLock(name)) {
// 1. 检查缓存
Class<?> clazz = findLoadedClass(name);
if (clazz != null) return clazz;

// 2. 尝试自己加载(从WEB-INF/lib)
try {
clazz = findClass(name);
System.out.println(appName + " 自己加载了: " + name);
return clazz;
} catch (ClassNotFoundException e) {
// 3. 自己找不到,委托父加载器
System.out.println(appName + " 委托父加载器: " + name);
return getParent().loadClass(name);
}
}
}
}

public static void main(String[] args) {
System.out.println("=== Tomcat类加载隔离演示 ===\\n");

// 创建两个Web应用各自的类加载器
WebappClassLoader appALoader = new WebappClassLoader(
"AppA", "/webapps/appA/WEB-INF/lib",
ClassLoader.getSystemClassLoader()
);

WebappClassLoader appBLoader = new WebappClassLoader(
"AppB", "/webapps/appB/WEB-INF/lib",
ClassLoader.getSystemClassLoader()
);

System.out.println("隔离效果:");
System.out.println("AppA的类加载器: " + appALoader);
System.out.println("AppB的类加载器: " + appBLoader);
System.out.println("\\n两个WebappClassLoader加载的同一类名");
System.out.println("在JVM中是不同的Class对象!");
System.out.println("这就是应用隔离的核心机制");
}
}

三、JDBC如何打破双亲委派: SPI机制的反向调用

3.1 JDBC的SPI困境

MySQL驱动(Application层)SPI机制DriverManager(Bootstrap层)应用代码MySQL驱动(Application层)SPI机制DriverManager(Bootstrap层)应用代码#publish-mermaid-1784692885197-2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1784692885197-2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885197-2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885197-2 .error-icon{fill:#552222;}#publish-mermaid-1784692885197-2 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1784692885197-2 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1784692885197-2 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1784692885197-2 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1784692885197-2 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1784692885197-2 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1784692885197-2 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1784692885197-2 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1784692885197-2 .marker.cross{stroke:#333333;}#publish-mermaid-1784692885197-2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1784692885197-2 p{margin:0;}#publish-mermaid-1784692885197-2 .actor{stroke:#9370DB;fill:#ECECFF;stroke-width:1;}#publish-mermaid-1784692885197-2 rect.actor.outer-path[data-look=\”neo\”]{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 rect.note[data-look=\”neo\”]{stroke:#aaaa33;fill:#fff5ad;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 text.actor>tspan{fill:black;stroke:none;}#publish-mermaid-1784692885197-2 .actor-line{stroke:#9370DB;}#publish-mermaid-1784692885197-2 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#publish-mermaid-1784692885197-2 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#publish-mermaid-1784692885197-2 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#publish-mermaid-1784692885197-2 [id$=\”-arrowhead\”] path{fill:#333;stroke:#333;}#publish-mermaid-1784692885197-2 .sequenceNumber{fill:white;}#publish-mermaid-1784692885197-2 [id$=\”-sequencenumber\”]{fill:#333;}#publish-mermaid-1784692885197-2 [id$=\”-crosshead\”] path{fill:#333;stroke:#333;}#publish-mermaid-1784692885197-2 .messageText{fill:#333;stroke:none;}#publish-mermaid-1784692885197-2 .labelBox{stroke:#9370DB;fill:#ECECFF;filter:none;}#publish-mermaid-1784692885197-2 .labelText,#publish-mermaid-1784692885197-2 .labelText>tspan{fill:black;stroke:none;}#publish-mermaid-1784692885197-2 .loopText,#publish-mermaid-1784692885197-2 .loopText>tspan{fill:black;stroke:none;}#publish-mermaid-1784692885197-2 .sectionTitle,#publish-mermaid-1784692885197-2 .sectionTitle>tspan{fill:black;stroke:none;}#publish-mermaid-1784692885197-2 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#9370DB;fill:#9370DB;}#publish-mermaid-1784692885197-2 .note{stroke:#aaaa33;fill:#fff5ad;}#publish-mermaid-1784692885197-2 .noteText,#publish-mermaid-1784692885197-2 .noteText>tspan{fill:black;stroke:none;font-weight:normal;}#publish-mermaid-1784692885197-2 .activation0{fill:#f4f4f4;stroke:#666;}#publish-mermaid-1784692885197-2 .activation1{fill:#f4f4f4;stroke:#666;}#publish-mermaid-1784692885197-2 .activation2{fill:#f4f4f4;stroke:#666;}#publish-mermaid-1784692885197-2 .actorPopupMenu{position:absolute;}#publish-mermaid-1784692885197-2 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#publish-mermaid-1784692885197-2 .actor-man circle,#publish-mermaid-1784692885197-2 line{fill:#ECECFF;stroke-width:2px;}#publish-mermaid-1784692885197-2 g rect.rect{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));stroke:#9370DB;}#publish-mermaid-1784692885197-2 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node rect,#publish-mermaid-1784692885197-2 [data-look=\”neo\”].cluster rect,#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885197-2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}DriverManager在Bootstrap层MySQL驱动在Application层问题: 父加载器(Bootstrap)无法看到子加载器(Application)的类!Class.forName("com.mysql.cj.jdbc.Driver")需要使用线程上下文类加载器Thread.currentThread().getContextClassLoader()通过上下文类加载器加载驱动返回驱动实例注册驱动成功

3.2 线程上下文类加载器: 打破双亲委派的关键

/**
* 线程上下文类加载器 (Thread Context ClassLoader)
*
* 这是JDK提供的"后门"机制
* 允许父加载器请求子加载器加载类
*/
public class ThreadContextClassLoader {

public static void main(String[] args) {
System.out.println("=== 线程上下文类加载器 ===\\n");

// 获取当前线程的上下文类加载器
ClassLoader contextClassLoader =
Thread.currentThread().getContextClassLoader();

System.out.println("默认上下文类加载器: " + contextClassLoader);
System.out.println(" 通常是AppClassLoader(Application ClassLoader)\\n");

System.out.println("工作原理:");
System.out.println("1. Bootstrap ClassLoader 无法向下看到 AppClassLoader");
System.out.println("2. 但可以通过Thread获取当前线程的ContextClassLoader");
System.out.println("3. 这个ClassLoader默认是AppClassLoader");
System.out.println("4. Bootstrap层代码通过它访问应用层的类\\n");

System.out.println("这就是为什么SPI机制需要它:");
System.out.println("- DriverManager在Bootstrap层");
System.out.println("- 数据库驱动在Application层");
System.out.println("- DriverManager通过ContextClassLoader加载驱动");
}
}

3.3 JDBC驱动的加载流程详解

/**
* JDBC Driver加载的完整流程
*
* JDK 6+使用ServiceLoader机制自动加载驱动
* 不再需要手动Class.forName()
*/
public class JDBCloadingProcess {

public static void main(String[] args) {
System.out.println("=== JDBC驱动加载流程 ===\\n");

System.out.println("Step 1: 应用调用 DriverManager.getConnection()");
System.out.println(" DriverManager位于: Bootstrap ClassLoader\\n");

System.out.println("Step 2: DriverManager初始化时调用 loadInitialDrivers()");
System.out.println(" loadInitialDrivers() 使用 ServiceLoader 扫描 META-INF/services/\\n");

System.out.println("Step 3: ServiceLoader 使用线程上下文类加载器");
System.out.println(" Thread.currentThread().getContextClassLoader()");
System.out.println(" 通常是: AppClassLoader\\n");

System.out.println("Step 4: 扫描 META-INF/services/java.sql.Driver");
System.out.println(" 文件内容: com.mysql.cj.jdbc.Driver");
System.out.println(" 通过AppClassLoader加载MySQL驱动类\\n");

System.out.println("Step 5: 加载驱动类时自动执行static代码块");
System.out.println(" static {");
System.out.println(" DriverManager.registerDriver(new Driver());");
System.out.println(" }");
System.out.println(" 驱动注册到DriverManager中\\n");

System.out.println("Step 6: DriverManager使用已注册的驱动创建连接");
}
}

3.4 模拟JDBC打破双亲委派的代码

/**
* 模拟JDBC使用线程上下文类加载器打破双亲委派
*/
public class JDBCDelegationBreakingDemo {

// 模拟Bootstrap层的DriverManager
static class DriverManager {
// 这是Bootstrap层的代码
// 它本来无法看到Application层的MySQL驱动

public static void loadDriver() {
System.out.println("DriverManager(Bootstrap层): 开始加载驱动");

// 关键: 使用线程上下文类加载器
ClassLoader contextLoader =
Thread.currentThread().getContextClassLoader();

System.out.println(" 使用的上下文类加载器: " + contextLoader);
System.out.println(" 这通常是AppClassLoader\\n");

try {
// 通过上下文类加载器加载驱动
Class<?> driverClass = contextLoader.loadClass(
"JDBCDelegationBreakingDemo$MySQLDriver"
);
System.out.println(" 成功加载驱动: " + driverClass.getName());

// 反射调用驱动的注册方法
Object driver = driverClass.getDeclaredConstructor().newInstance();
System.out.println(" 驱动实例已创建: " + driver);

} catch (Exception e) {
System.err.println(" 加载失败: " + e.getMessage());
}
}
}

// 模拟应用层的MySQL驱动
static class MySQLDriver {
static {
System.out.println(" MySQLDriver static块执行");
System.out.println(" 驱动自动注册到DriverManager");
}

public MySQLDriver() {
System.out.println(" MySQLDriver实例化");
}
}

public static void main(String[] args) {
System.out.println("=== JDBC打破双亲委派演示 ===\\n");

// 设置线程上下文类加载器为AppClassLoader
Thread.currentThread().setContextClassLoader(
JDBCDelegationBreakingDemo.class.getClassLoader()
);

System.out.println("模拟Bootstrap层的DriverManager调用:\\n");
DriverManager.loadDriver();

System.out.println("\\n=== 结论 ===");
System.out.println("DriverManager(Bootstrap层)通过线程上下文类加载器");
System.out.println("成功加载了Application层的MySQL驱动");
System.out.println("这就是对双亲委派模型的一种'破坏'");
}
}

四、破坏双亲委派的其他典型案例

4.1 OSGi的网状类加载器

/**
* OSGi的类加载机制
*
* OSGi完全抛弃了双亲委派模型
* 改用基于Bundle的网状类加载结构
* 每个Bundle有自己的ClassLoader
* Bundle之间可以互相导入导出包
*/
public class OSGiClassLoading {

public static void main(String[] args) {
System.out.println("=== OSGi类加载 ===\\n");

System.out.println("OSGi的类加载特点:");
System.out.println("1. 每个Bundle(模块)有独立的ClassLoader");
System.out.println("2. Bundle声明导出(Export-Package)和导入(Import-Package)");
System.out.println("3. 类加载不再是父子层次,而是网状依赖");
System.out.println("4. 支持模块热插拔和动态更新\\n");

System.out.println("网状结构示例:");
System.out.println(" Bundle-A 导出 com.example.api");
System.out.println(" Bundle-B 导入 com.example.api(来自Bundle-A)");
System.out.println(" Bundle-C 导入 com.example.api(来自Bundle-A)");
System.out.println(" 这完全不同于双亲委派的层次结构");
}
}

4.2 Java模块化系统(JPMS)的改进

/**
* Java 9+ 模块化系统(JPMS)
*
* JPMS对双亲委派做了重要改进
* 在保持层次结构的同时增加了模块边界
*/
public class JPMSImprovement {

public static void main(String[] args) {
System.out.println("=== JPMS(Java模块化系统) ===\\n");

System.out.println("JPMS对类加载的改进:");
System.out.println("1. 保留双亲委派的基本架构");
System.out.println("2. 增加模块描述符(module-info.java)");
System.out.println("3. 严格控制模块间的访问权限");
System.out.println("4. 解决了JAR地狱的部分问题\\n");

System.out.println("相比OSGi:");
System.out.println("- JPMS: 静态模块化,编译时检查");
System.out.println("- OSGi: 动态模块化,运行时绑定\\n");

System.out.println("相比经典双亲委派:");
System.out.println("- 增加了模块可见性控制");
System.out.println("- 即使在同一ClassLoader中,不同模块也可能隔离");
}
}

五、总结: "破坏"是一种进化

5.1 三种破坏模式对比

#publish-mermaid-1784692885228-3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1784692885228-3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885228-3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1784692885228-3 .error-icon{fill:#552222;}#publish-mermaid-1784692885228-3 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1784692885228-3 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1784692885228-3 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1784692885228-3 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1784692885228-3 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1784692885228-3 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1784692885228-3 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1784692885228-3 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1784692885228-3 .marker.cross{stroke:#333333;}#publish-mermaid-1784692885228-3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-1784692885228-3 p{margin:0;}#publish-mermaid-1784692885228-3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-1784692885228-3 .cluster-label text{fill:#333;}#publish-mermaid-1784692885228-3 .cluster-label span{color:#333;}#publish-mermaid-1784692885228-3 .cluster-label span p{background-color:transparent;}#publish-mermaid-1784692885228-3 .label text,#publish-mermaid-1784692885228-3 span{fill:#333;color:#333;}#publish-mermaid-1784692885228-3 .node rect,#publish-mermaid-1784692885228-3 .node circle,#publish-mermaid-1784692885228-3 .node ellipse,#publish-mermaid-1784692885228-3 .node polygon,#publish-mermaid-1784692885228-3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885228-3 .rough-node .label text,#publish-mermaid-1784692885228-3 .node .label text,#publish-mermaid-1784692885228-3 .image-shape .label,#publish-mermaid-1784692885228-3 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1784692885228-3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1784692885228-3 .rough-node .label,#publish-mermaid-1784692885228-3 .node .label,#publish-mermaid-1784692885228-3 .image-shape .label,#publish-mermaid-1784692885228-3 .icon-shape .label{text-align:center;}#publish-mermaid-1784692885228-3 .node.clickable{cursor:pointer;}#publish-mermaid-1784692885228-3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1784692885228-3 .arrowheadPath{fill:#333333;}#publish-mermaid-1784692885228-3 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1784692885228-3 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1784692885228-3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885228-3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885228-3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885228-3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1784692885228-3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1784692885228-3 .cluster text{fill:#333;}#publish-mermaid-1784692885228-3 .cluster span{color:#333;}#publish-mermaid-1784692885228-3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1784692885228-3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1784692885228-3 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1784692885228-3 .icon-shape,#publish-mermaid-1784692885228-3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1784692885228-3 .icon-shape p,#publish-mermaid-1784692885228-3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1784692885228-3 .icon-shape .label rect,#publish-mermaid-1784692885228-3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1784692885228-3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1784692885228-3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1784692885228-3 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node rect,#publish-mermaid-1784692885228-3 [data-look=\”neo\”].cluster rect,#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885228-3 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1784692885228-3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}OSGi模式: 网状

Bundle-A

Bundle-B

Bundle-C

JDBC模式: 反向调用通过线程上下文类加载器访问

DriverManagerBootstrap层

MySQL驱动Application层

Tomcat模式: 反向优先优先自己加载自己加载失败才委派

父加载器System/Common

WebappClassLoader

经典双亲委派

父加载器

子加载器

5.2 破坏动机总结表

| 框架 | 破坏方式 | 破坏动机 | 解决的问题 |

|——|———|———|———–|

| Tomcat | 重写loadClass,优先自己加载 | 应用隔离 | 不同版本库共存 |

| JDBC | 线程上下文类加载器 | SPI反向可见性 | 父加载器访问子加载器的类 |

| OSGi | 网状类加载结构 | 模块化+动态化 | 热插拔、版本管理 |

| JPMS | 模块描述符 | 强封装 | 模块边界与访问控制 |

5.3 核心认知

  • 双亲委派是安全基线,但不是性能或灵活性的最优解
  • "破坏"不是否定,而是为了满足特定场景的需求
  • Tomcat的破坏实现应用隔离,JDBC的破坏实现SPI机制
  • 理解破坏的动机比记住破坏的方式更重要
  • Java 9+的JPMS是在双亲委派基础上的进化而非颠覆
  • 如果本文帮你理解了框架"破坏"双亲委派的设计动机,欢迎点赞收藏。有任何疑问,欢迎评论区交流!

    赞(0)
    未经允许不得转载:171主机测评 » 双亲委派模型破坏: Tomcat和JDBC驱动是如何打破双亲委派机制的?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址