前言
最近在技术讨论群里面看到的这样的一个问题
是关于类加载器的, 这块用到的不多
呵呵 但是 inspect 一下, 还是挺有意思的
本文主要讨论的是 springboot 项目的 ide启动 和 jar启动 的一些差异
本文主要以一下几点来展开
1. ide启动 和 jar启动 的类加载器的差异
2. 提问的朋友说是 jdk8 没有问题, 尝试一下 jdk8 的情况
3. springboot jar启动 类加载的方式
4. jar启动 调整 AppClassLoader 的 classpath ?
以下设计运行时环境的信息, 会在开头描述
下文中 ucp 指代的是 urlClassPath
测试用例
测试用例来自于提问的朋友, 我只是略微调整了一下
新建一个 springboot 项目, 复制粘贴如下测试用例, 增加依赖 jol[或者删除 VM 部分代码], 调整 package 理论上来说就够了
package com.hx.boot.controller;
/**
* Test01AsyncLoadClassController
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-04-10 15:05
*/
@RestController
@RequestMapping("/test01AsyncLoadClass")
public class Test01AsyncLoadClassController {
// 问题描述 : 直接在 idea 里面启动项目, AppClassLoader 可以正常的加载 ConditionalOnBean
// 但是如果是 java -jar hellospringboot-0.0.1.jar, 就加载不了 ConditionalOnBean 了
// jdk1.8 两种都可以 ?
@GetMapping("/async")
public String async() {
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(2000);
doBiz();
} catch (Exception e) {
e.printStackTrace();
}
});
try {
doBiz();
} catch (Exception e) {
e.printStackTrace();
return "EX";
}
return "OK";
}
private void doBiz() throws Exception {
String threadName = Thread.currentThread().getName();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String contextClassloader = classLoader.toString();
System.out.println(threadName + " -> " + contextClassloader);
VirtualMachine vm = VM.current();
System.out.println(vm.addressOf(classLoader));
System.out.println(System.getProperty("java.class.path"));
Class clazz = classLoader.loadClass("org.springframework.boot.autoconfigure.condition.ConditionalOnBean");
// Field ucpField = classLoader.getClass().getDeclaredField("ucp");
// ucpField.setAccessible(true);
// Object ucp = ucpField.get(classLoader);
// Field pathListField = ucp.getClass().getDeclaredField("path");
// pathListField.setAccessible(true);
// Object path = pathListField.get(ucp);
// System.out.println(" ucp pathList -> " + path.toString());
System.out.println(" loadClass -> " + clazz.getName());
}
}
04.11 的 inspect
以下调试基于 jdk9 + spring-boot-2.4.4
当然 这里更关心的是 CompletableFuture.runAsync 里面处理的逻辑, 以两种方式启动 查看了一下运行时的 类加载的 classpath 信息
ForkJoin.commonPool 里面的线程的 classloader 为 AppClassLoader
第一幅图是 ide 里面启动的时候 AppClassLoader 的 classpath 相关信息
第二幅图是 java -jar 里面启动的时候 AppClassLoader 的 classpath 相关信息
应该是 两种方式对于 AppClassLoader 有一定的影响吧
1. ide启动 和 jar启动 的类加载器的差异
以下调试基于 jdk9 + spring-boot-2.4.4
对于这里, 我们更关心的就是 TomcatEmbeddedWebappClassLoader 的依赖层级, 以及 ClassLoaders$AppClassLoader 的 urlClassPath
以下为日志, 稍微看一下
case1. ide启动 + http处理线程, contextClassLoader 为 TomcatEmbeddedWebappClassLoader 的 parent 是 ClassLoaders$AppClassLoader, 将类加载业务委托给 ClassLoaders$AppClassLoader, 加载 ConditionalOnBean
case2. ide启动 + ForkJoin.commonPool线程 contextClassLoader 为 ClassLoaders$AppClassLoader, 加载 ConditionalOnBean
case3. jar启动 + http处理线程, contextClassLoader 为 TomcatEmbeddedWebappClassLoader 的 parent 是 LaunchedURLClassLoader 将类加载业务委托给 LaunchedURLClassLoader, 加载 ConditionalOnBean
case4. jar启动 + ForkJoin.commonPool线程 contextClassLoader 为 ClassLoaders$AppClassLoader, classpath 中无关联 spring-boot-autoconfigure-2.1.1.RELEASE.jar, 加载失败
# 1. idea 启动
# TomcatEmbeddedWebappClassLoader[none] -> ClassLoaders$AppClassLoader[all deps]
http-nio-8080-exec-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
jdk.internal.loader.ClassLoaders$AppClassLoader@4f8e5cde
31207551144
/Users/jerry/IdeaProjects/HelloSpringBoot/target/classes:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.4.4/spring-boot-starter-web-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.4/spring-boot-starter-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot/2.4.4/spring-boot-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4.4/spring-boot-autoconfigure-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4.4/spring-boot-starter-logging-2.4.4.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/jerry/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/jerry/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/jerry/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4.4/spring-boot-starter-json-2.4.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4.4/spring-boot-starter-tomcat-2.4.4.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.44/tomcat-embed-core-9.0.44.jar:/Users/jerry/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.44/tomcat-embed-websocket-9.0.44.jar:/Users/jerry/.m2/repository/org/springframework/spring-web/5.3.5/spring-web-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-beans/5.3.5/spring-beans-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-webmvc/5.3.5/spring-webmvc-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-aop/5.3.5/spring-aop-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-context/5.3.5/spring-context-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-expression/5.3.5/spring-expression-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-loader/2.4.4/spring-boot-loader-2.4.4.jar:/Users/jerry/.m2/repository/org/projectlombok/lombok/1.18.18/lombok-1.18.18.jar:/Users/jerry/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/jerry/.m2/repository/org/springframework/spring-core/5.3.5/spring-core-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-jcl/5.3.5/spring-jcl-5.3.5.jar:/Users/jerry/.m2/repository/org/openjdk/jol/jol-core/0.8/jol-core-0.8.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
ForkJoinPool.commonPool-worker-9 -> jdk.internal.loader.ClassLoaders$AppClassLoader@4f8e5cde
31140483584
/Users/jerry/IdeaProjects/HelloSpringBoot/target/classes:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.4.4/spring-boot-starter-web-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.4/spring-boot-starter-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot/2.4.4/spring-boot-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4.4/spring-boot-autoconfigure-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4.4/spring-boot-starter-logging-2.4.4.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/jerry/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/jerry/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/jerry/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4.4/spring-boot-starter-json-2.4.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4.4/spring-boot-starter-tomcat-2.4.4.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.44/tomcat-embed-core-9.0.44.jar:/Users/jerry/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.44/tomcat-embed-websocket-9.0.44.jar:/Users/jerry/.m2/repository/org/springframework/spring-web/5.3.5/spring-web-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-beans/5.3.5/spring-beans-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-webmvc/5.3.5/spring-webmvc-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-aop/5.3.5/spring-aop-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-context/5.3.5/spring-context-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-expression/5.3.5/spring-expression-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-loader/2.4.4/spring-boot-loader-2.4.4.jar:/Users/jerry/.m2/repository/org/projectlombok/lombok/1.18.18/lombok-1.18.18.jar:/Users/jerry/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/jerry/.m2/repository/org/springframework/spring-core/5.3.5/spring-core-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-jcl/5.3.5/spring-jcl-5.3.5.jar:/Users/jerry/.m2/repository/org/openjdk/jol/jol-core/0.8/jol-core-0.8.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
# 2. java -jar 启动
# TomcatEmbeddedWebappClassLoader[none] -> LaunchedURLClassLoader[all deps] -> ClassLoaders$AppClassLoader[hellospringboot.jar]
http-nio-8080-exec-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@4566e5bd
31145557128
/Users/jerry/IdeaProjects/HelloSpringBoot/target/hellospringboot-0.0.1.jar
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
ForkJoinPool.commonPool-worker-9 -> jdk.internal.loader.ClassLoaders$AppClassLoader@4f8e5cde
31138713504
/Users/jerry/IdeaProjects/HelloSpringBoot/target/hellospringboot-0.0.1.jar
java.lang.ClassNotFoundException: org.springframework.boot.autoconfigure.condition.ConditionalOnBean
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
at com.hx.boot.controller.Test01AsyncLoadClassController.doBiz(Test01AsyncLoadClassController.java:58)
at com.hx.boot.controller.Test01AsyncLoadClassController.lambda$async$0(Test01AsyncLoadClassController.java:31)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:283)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1603)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:175)
TomcatEmbeddedWebappClassLoader的依赖层级 以及 ClassLoaders$AppClassLoader的ucp信息
看完之后, 当然这里对于这个问题得出的结论是和 "04.11 的 inspect " 一致的, 只是这里会有一个更加细节的认知
1. ide 启动的时候 TomcatEmbeddedWebappClassLoader 层级如下, 以及 AppClassLoader 的 ucp 信息也可以看到
TomcatEmbeddedWebappClassLoader[none] -> ClassLoaders$AppClassLoader[all deps]
可以看到这里的 AppClassLoader 的 classpath 是包含了项目运行依赖的所有的 classes, jar
2. jar 启动的时候 TomcatEmbeddedWebappClassLoader 层级如下, 以及 AppClassLoader 的 ucp 信息也可以看到
TomcatEmbeddedWebappClassLoader[none] -> LaunchedURLClassLoader[all deps] -> ClassLoaders$AppClassLoader[hellospringboot.jar]
可以看到这里的 LaunchedURLClassLoader 的 classpath 是包含了项目运行依赖的所有的 classes, jar
可以看到这里的 ClassLoaders$AppClassLoader 的 classpath 是包含了项目运行的 jar 包 hellospringboot-0.0.1.jar
对于这里的 TomcatEmbeddedWebappClassLoader 的初始化扩展一下
1. ide 启动的时候, StandardContext 初始化的时候创建 WebappClassLoader, 传入的 parent 为 context.parentClassLoader
context 初始化的时候, 初始化 parentClassLoader
2. jar 启动的时候, StandardContext 初始化的时候创建 WebappClassLoader, 传入的 parent 为 context.parentClassLoader
context 初始化的时候, 初始化 parentClassLoader
对于这里的 LaunchedURLClassLoader 的初始化扩展一下
LaunchedURLClassLoader 的初始化, 传入的 url 列表即为我们 spring-boot.jar 里面的 BOOT-INF 下面的所有的 classes, jar
url 列表的初始化, 查询的是 archive 里面的 BOOT-INF/classes/ 文件夹, 以及 BOOT-INF/lib 下面的包
2. 提问的朋友说是 jdk8 没有问题, 尝试一下 jdk8 的情况
呵呵 这个我之前也是没有试过的, 记录的时候实时操作操作的, 呵呵 有点震惊
稍微看一下吧, 主要的差异在 ForkJoin.commonPool 里面的线程的 contextClassloader 是 TomcatEmbeddedWebappClassLoader
而在 jdk9 的环境中, ForkJoin.commonPool 里面的线程的 contextClassloader 是 ClassLoaders$AppClassLoader
# 1. idea 启动
# TomcatEmbeddedWebappClassLoader[none] -> ClassLoaders$AppClassLoader[all deps]
http-nio-8080-exec-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
sun.misc.Launcher$AppClassLoader@18b4aac2
32715886360
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/tools.jar:/Users/jerry/IdeaProjects/HelloSpringBoot/target/classes:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.4.4/spring-boot-starter-web-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.4/spring-boot-starter-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot/2.4.4/spring-boot-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4.4/spring-boot-autoconfigure-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4.4/spring-boot-starter-logging-2.4.4.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/jerry/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/jerry/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/jerry/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4.4/spring-boot-starter-json-2.4.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4.4/spring-boot-starter-tomcat-2.4.4.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.44/tomcat-embed-core-9.0.44.jar:/Users/jerry/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.44/tomcat-embed-websocket-9.0.44.jar:/Users/jerry/.m2/repository/org/springframework/spring-web/5.3.5/spring-web-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-beans/5.3.5/spring-beans-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-webmvc/5.3.5/spring-webmvc-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-aop/5.3.5/spring-aop-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-context/5.3.5/spring-context-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-expression/5.3.5/spring-expression-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-loader/2.4.4/spring-boot-loader-2.4.4.jar:/Users/jerry/.m2/repository/org/projectlombok/lombok/1.18.18/lombok-1.18.18.jar:/Users/jerry/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/jerry/.m2/repository/org/springframework/spring-core/5.3.5/spring-core-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-jcl/5.3.5/spring-jcl-5.3.5.jar:/Users/jerry/.m2/repository/org/openjdk/jol/jol-core/0.8/jol-core-0.8.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Users/jerry/Library/Caches/IntelliJIdea2019.1/captureAgent/debugger-agent.jar
ForkJoinPool.commonPool-worker-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
sun.misc.Launcher$AppClassLoader@18b4aac2
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
32715886360
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/tools.jar:/Users/jerry/IdeaProjects/HelloSpringBoot/target/classes:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.4.4/spring-boot-starter-web-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.4/spring-boot-starter-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot/2.4.4/spring-boot-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4.4/spring-boot-autoconfigure-2.4.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4.4/spring-boot-starter-logging-2.4.4.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/jerry/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar:/Users/jerry/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/jerry/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/jerry/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/jerry/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4.4/spring-boot-starter-json-2.4.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar:/Users/jerry/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4.4/spring-boot-starter-tomcat-2.4.4.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.44/tomcat-embed-core-9.0.44.jar:/Users/jerry/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/jerry/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.44/tomcat-embed-websocket-9.0.44.jar:/Users/jerry/.m2/repository/org/springframework/spring-web/5.3.5/spring-web-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-beans/5.3.5/spring-beans-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-webmvc/5.3.5/spring-webmvc-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-aop/5.3.5/spring-aop-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-context/5.3.5/spring-context-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-expression/5.3.5/spring-expression-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/boot/spring-boot-loader/2.4.4/spring-boot-loader-2.4.4.jar:/Users/jerry/.m2/repository/org/projectlombok/lombok/1.18.18/lombok-1.18.18.jar:/Users/jerry/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/Users/jerry/.m2/repository/org/springframework/spring-core/5.3.5/spring-core-5.3.5.jar:/Users/jerry/.m2/repository/org/springframework/spring-jcl/5.3.5/spring-jcl-5.3.5.jar:/Users/jerry/.m2/repository/org/openjdk/jol/jol-core/0.8/jol-core-0.8.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Users/jerry/Library/Caches/IntelliJIdea2019.1/captureAgent/debugger-agent.jar
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
# 2. java -jar 启动
# TomcatEmbeddedWebappClassLoader[none] -> LaunchedURLClassLoader[all deps] -> ClassLoaders$AppClassLoader[hellospringboot.jar]
http-nio-8080-exec-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@59e84876
32581085512
/Users/jerry/IdeaProjects/HelloSpringBoot/target/hellospringboot-0.0.1.jar:/Users/jerry/Library/Caches/IntelliJIdea2019.1/captureAgent/debugger-agent.jar
ForkJoinPool.commonPool-worker-1 -> TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@59e84876
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
32581085512
/Users/jerry/IdeaProjects/HelloSpringBoot/target/hellospringboot-0.0.1.jar:/Users/jerry/Library/Caches/IntelliJIdea2019.1/captureAgent/debugger-agent.jar
loadClass -> org.springframework.boot.autoconfigure.condition.ConditionalOnBean
jdk8 中初始化的时候, 是 http 处理线程来初始化 ForkjoinWorkerThread, 这里的 parent 为当前线程, 即为 http 处理线程
获取他的 contextClassLoader 作为新建的 ForkjoinWorkerThread.contextClassLoader
jdk9 中初始化的时候, 是 http 处理线程来初始化 ForkjoinWorkerThread, 这里的 parent 为当前线程, 即为 http 处理线程
获取他的 contextClassLoader 作为新建的 ForkjoinWorkerThread.contextClassLoader
然后多了一个步骤是, 初始化之后设置了 contextClassLoader 为 ClassLoaders$AppClassLoader
3. springboot jar启动 类加载的方式
这里具体请调试 LaunchedURLClassLoader 相关代码, 处理很"优雅"
我这里整理了一个 从 jar 中的 jar 读取 class 的方式的一个 demo
package com.hx.test11;
/**
* Test24ReadClassInJar
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-04-11 06:04
*/
public class Test24ReadClassInJar {
// Test24ReadClassInJar
public static void main(String[] args) throws Exception {
String mainJarPath = "/Users/jerry/IdeaProjects/HelloSpringBoot/target/hellospringboot-0.0.1.jar";
String autoconfigurePath = "BOOT-INF/lib/spring-boot-autoconfigure-2.4.4.jar";
String onConditionalPath = "org/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.class";
String autoconfigureNestedPath = "file:" + mainJarPath + "!/" + autoconfigurePath + "!/";
JarFile mainJarFile = new JarFile(new File(mainJarPath));
JarFile autoconfigureFile = mainJarFile.getNestedJarFile(mainJarFile.getJarEntry(autoconfigurePath));
// read from URL
URL autoconfigureJar = new URL("jar", "", -1, autoconfigureNestedPath, new Handler(autoconfigureFile));
URL url = new URL(autoconfigureJar, onConditionalPath);
URLConnection connection = url.openConnection();
InputStream is = connection.getInputStream();
byte[] magicBytes = new byte[4];
is.read(magicBytes);
for (byte b : magicBytes) {
System.out.print("0x" + Integer.toHexString(b).substring(6) + " ");
}
System.out.println();
// read from JarFile
JarEntry onConditionalEntry = autoconfigureFile.getJarEntry(onConditionalPath);
InputStream is2 = autoconfigureFile.getInputStream(onConditionalEntry);
is2.read(magicBytes);
for (byte b : magicBytes) {
System.out.print("0x" + Integer.toHexString(b).substring(6) + " ");
}
}
}
期望输出结果如下
4. jar启动 调整 AppClassLoader 的 classpath ?
todo
完