65 SpringBoot项目 ide启动 和 jar启动 AppClassLoader类加载器问题

前言

最近在技术讨论群里面看到的这样的一个问题 

是关于类加载器的, 这块用到的不多 

呵呵 但是 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 

 

 

完 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值