SpringBoot: 启动流程和类装载

前面我们学过Spring定制了自己的可执行jar,将真正执行时需要的类和依赖放到BOOT-INF/classes、BOOT-INF/lib来,为了能够识别这些为止的源文件,Spring定制了自己类加载器,本节我们来讲解这个类加载器。本节涉及的内容主要包括:

  1. SpringBoot启动流程
  2. SpringBoot类加载器

1. SpringBoot启动流程

在SpringBoot的可执行jar中,配置了MANIFEST.MF的Main-Class设置为JarLauncher,JarLauncher的加载过程如下图

  1. 调用Launcher.launch方法,开始引导程序
  2. 通过Handlers.reigster()注册URLStreamHandler的jar协议处理类,支持getResource读取BOOT-INF/下的内容
  3. 通过archive.getClassPathUrls()筛选可执行jar的所有元素,通过JarLauncher.isIncludedOnClassPath过滤,筛选出所有ClassPath元素
  4. 用这些筛选的ClassPath创建LaunchedClassLoader
  5. 使用LaunchedClassLoader加载MANIFEST.MF的Start-Class
  6. 反射调用main方法

2. SpringBoot类加载器

使用URLClassLoader加载SpringBoot Jar时不会加载BOOT-INF下的classes、lib的类和依赖包,SpringBoot通过自定义类加载器LaunchedClassLoader解决这个问题。它继承自URLClassLoader。参照SpringBoot的启动流程,我们讲手动使用LaunchedClassLoader加载可执行jar,并尝试引导SpringBoot应用。

1. 创建Archive

从启动流程可以看到,核心流程是先通过Archive.getClassPathUrls()获取所有的ClassPath,包括jar文件中内嵌的。JarFileArchive是包可见的,用Archive.create创建实例

URLClassLoader ucl = new URLClassLoader("MyURLClassLoader", new URL[]{url}, null);
Class<?> clazz = ucl.loadClass("org.springframework.boot.loader.JarLauncher");
Archive archive = Archive.create(clazz);
2. 枚举内嵌Jar

Archive提供了getClassPathUrls筛选所有的Archive.Entry,Archive和Archive.Entry可以按JarFile、JarEntry来理解,正是它们有不同的抽象,不仅仅支持jar文件。

Set<URL> su = archive.getClassPathUrls((Archive.Entry e) -> true);
su.stream().forEach(x -> System.out.println(x));

这个方法的输出是这样的,它会是一些jar:nested:协议的URL集合。我们知道URLClassLoader接收一组根路径实现类加载,把这个集合都传递给URLClassLoader是不是就可以了呢?

3. 使用内嵌Jar

讲上一步获取的Set作为根路劲提供给URLClassLoader

URLClassLoader lcl = new URLClassLoader("MyNested", su.toArray(URL[]::new),null);
clazz = lcl.loadClass("com.keyniu.yangsi.YangsiApplication");
System.out.println(clazz);

并不能正常工作,因为jar:nested不是Java支持的内嵌协议

4. 注册协议处理器

在SpringBoot的启动流程里我们专门有提到过Handlers,它就是用来注册协议处理器的在使用3.3.3的代码之前,执行如下代码,就能执行成功了。

Handlers.register();

Handlers.register()实际是通过包名: org.springframework.boot.loader.net.protocol,注册了jar、nested(包名)这两个协议的处理器Handler(对于包下固定类名)

5. 启动SpringBoot

现在我们已经能完整的加载整个SpringBoot可以执行jar了,我们是不是能像JarLauncher一样调用Start-Class的main方法来启动应用呢?答案是肯定的。

private static void testLaunched(URL url) throws Exception {
    URLClassLoader ucl = new URLClassLoader("MyURLClassLoader", new URL[]{url}, null);
    Class<?> clazz = ucl.loadClass("org.springframework.boot.loader.JarLauncher");
    Archive archive = Archive.create(clazz);
    Set<URL> su = archive.getClassPathUrls((Archive.Entry e) -> true);

    Handlers.register();
    su.add(url);

    LaunchedClassLoader lcl = new LaunchedClassLoader(false, su.toArray(URL[]::new), null);

    clazz = Class.forName("com.keyniu.yangsi.YangsiApplication", false, lcl);               // 启动类
    Method mainMethod = clazz.getDeclaredMethod("main", String[].class);
                    // main方法
    mainMethod.setAccessible(true);

    mainMethod.invoke(null, (Object) new String[0]);                                        // 调用main方法
}

5. 参考资料

  1. JVM Specification: Loading、Linking And Initializing,Chapter 5. Loading, Linking, and Initializing
  2. SClass Loaders in Java | Baeldung

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/694564.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32F103单片机工程移植到航顺单片机HK32F103注意事项

一、简介 作为国内MCU厂商中前三阵营之一的航顺芯片&#xff0c;建立了世界首创超低功耗7nA物联网、万物互联核心处理器浩瀚天际10X系列平台&#xff0c;接受代理商/设计企业/方案商定制低于自主研发十倍以上成本&#xff0c;接近零风险自主品牌产品&#xff0c;芯片设计完成只…

HC-SR505人体感应灯

1硬件 1.1硬件组成 1.正点原子探索者开发板 2 HC-SR505迷你小型人体感应模块 3 继电器&#xff0b;5V小灯 HC-SR505迷你小型人体感应模块介绍 1.2 硬件连接 1.HC-SR505&#xff08;连接在PE0&#xff09; 2.继电器&#xff08;连接在PE1&#xff09; 2.主要代码 int ma…

ReactRouter——路由配置、路由跳转、带参跳转、新route配置项

目录 写在前面 (一)初步使用router 1.安装react-router-dom 2.创建router结构 3.嵌套路由 4.配置not found页面 (1)确切路由报错页面 (2)未配置路由报错页面 5.重定向 (二)路由跳转 1.组件跳转 2.NavLink 3.js跳转 (三)传递参数 1.searchParams(query)参数 2…

ubuntu使用docker安装openwrt

系统&#xff1a;ubuntu24.04 架构&#xff1a;x86 1. 安装docker 1.1 离线安装 docker下载地址 根据系统版本&#xff0c;依次下载最新的三个关于docker的软件包 container.io&#xff08;注意后缀版本顺序&#xff09;docker-ce-clidocker-ce 然后再ubuntu系统中依次按顺…

如何自动化地评估 AIGC 生图的质量?

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

【Spring Boot】异常处理

异常处理 1.认识异常处理1.1 异常处理的必要性1.2 异常的分类1.3 如何处理异常1.3.1 捕获异常1.3.2 抛出异常1.3.4 自定义异常 1.4 Spring Boot 默认的异常处理 2.使用控制器通知3.自定义错误处理控制器3.1 自定义一个错误的处理控制器3.2 自定义业务异常类3.2.1 自定义异常类3…

Cweek4+5

C语言学习 十.指针详解 6.有关函数指针的代码 代码1&#xff1a;(*(void (*)())0)(); void(*)()是函数指针类型&#xff0c;0是一个函数的地址 (void(*)())是强制转换 总的是调用0地址处的函数&#xff0c;传入参数为空 代码2&#xff1a;void (*signal(int, void(*)(int))…

AE电源pinnacle软件新款老款二款软件

AE电源pinnacle软件新款老款二款软件

高速USB转串口芯片CH343

CH343封装 截止目前&#xff0c;主要封装有 SOP16: CH343G QFN16: CH343P ESSOP10: CH343K,截止24年6月未生产 CH343串口速度 最高串口速度&#xff1a; 6Mbps,比CH340的2M&#xff0c;快3倍 1、概述 参考版本&#xff1a;1E CH343 是一个 USB 总线的转接芯片&#xff0c;…

国标GB/T 28181详解:校时流程详细说明

目录 一、定义 二、作用 1. 时间同步性 2. 事件记录的准确性 3. 跨平台、跨设备协作 4. 降低时间误差 5. 安全性提升 三、基本要求 四、命令流程 五、协议接口 六、校时效果 1、未校时的情况 2、校时后的效果 七、参考 一、定义 GB28181协议要求所有的监控设…

把系统引导做到U盘,实现插上U盘才能开机

前言 有个小伙伴提出了这样一个问题&#xff1a;能不能把U盘制作成电脑开机的钥匙&#xff1f; 小白稍微思考了一下&#xff0c;便做了这样一个回复&#xff1a;可以。 至于为什么要思考一下&#xff0c;这样会显得我有认真思考他提出的问题。 Windows7或以上系统均支持UEF…

Cannot access spring-snapshot (https://repo.spring.io/snapshot) in offline mode

Maven报错 这个选项是脱机工作&#xff0c;意思就是不读取远程仓库&#xff0c;只读取本地已有的仓库&#xff0c;之所以报错原因就是本地仓库是空的&#xff0c;然而选择了脱机工作

Android.mk文件生成的so工程文件并Debug调试native code

1.这里主要展示一下从最原始先新建一个工程 2.将hello的子工程文件放入上面新建好的工程里面&#xff0c;直接拷贝放置这里 3.修改根目录下的settings.gradle 加入hello 4.app工程下的build.gradle加入依赖&#xff0c;这样就可以识别hello中的java包文件 5.MainActivity 中来&…

Redis主从同步

master如何得知salve是否是第一次来同步呢&#xff1f;&#xff1f; 有几个概念&#xff0c;可以作为判断依据&#xff1a; Replication Id&#xff1a;简称replid&#xff0c;是数据集的标记&#xff0c;replid一致则是同一数据集。每个master都有唯一的replid&#xff0c;s…

C++初阶学习第六弹——探索STL奥秘(一)——标准库中的string类

前言&#xff1a; 在前面&#xff0c;我们学习了C的类与对象&#xff0c;认识到了C与C语言的一些不同&#xff0c;今天&#xff0c;我们将进入C的 关键部分——STL&#xff0c;学习完这部分之后&#xff0c;我们就可以清楚的认识到C相比于C语言的快捷与便利 目录 一、为什么有s…

【MySQL】(基础篇四) —— 检索数据

检索数据 检索数据是我们使用数据库时进行最多的操作&#xff0c;其中包括了检索条件、排序、过滤、分组等等。我会在后续的多篇博客中为你进行详细地介绍它们。 这次先让我们来粗略的了解一下SELECT&#xff0c;为了使用SELECT检索表数据&#xff0c;必须至少明确两点信息—…

Linux:动态库和静态库的编译与使用

目录 1.前言 2.静态链接库 3.静态链接库生成步骤 4.静态链接库的使用 5.动态链接库 6.动态链接库生成步骤 7.动态链接库的使用 8.动态链接库无法加载 9.解决动态链接库无法加载问题 前言 在《MinGW&#xff1a;从入门到链接库》博客中简单介绍了如何编译动态链接库和静态链接库…

python - pandas常用计算函数

文中所用数据集有需要的可以私聊我获取 学习目标 知道排序函数nlargest、nsmallest和sort_values的用法 知道Pandas中求和、计数、相关性值、最小、最大、平均数、标准偏差、分位数的函数使用 1 排序函数 导包并加载数据集 import pandas as pd ​ # 加载csv数据, 返回df对…

MySQL之多表查询—行子查询

一、引言 上篇博客学习了列子查询。 接下来学习子查询中的第三种——行子查询。 行子查询 1、概念 子查询返回的结果是一行&#xff08;当然可以是多列)&#xff0c;这种子查询称为行子查询。 2、常用的操作符 、 <> (不等于) 、IN 、NOT IN 接下来通过一个需求去演示和…

系统思考—心智模式

凯恩斯说&#xff1a;“介绍新观念倒不是很难&#xff0c;难的是清除那些旧观念。”在过去的任何一年&#xff0c;如果你一次都没有推翻过自己最中意的想法&#xff0c;那么你这一年就算浪费了。旧观念像是根深蒂固的杂草&#xff0c;即使在新知识的光照下&#xff0c;也需要时…