精尽Spring Boot源码分析 - Jar 包的启动实现

  1. BOOT-INF 目录,里面保存了我们自己 Spring Boot 项目编译后的所有文件,其中 classes 目录下面就是编译后的 .class 文件,包括项目中的配置文件等,lib 目录下就是我们引入的第三方依赖

  2. META-INF 目录,通过 MANIFEST.MF 文件提供 jar 包的元数据,声明 jar 的启动类等信息。每个 Java jar 包应该是都有这个文件的,参考 Oracle 官方对于 jar 的说明,里面有一个 Main-Class 配置用于指定启动类

  3. org.springframework.boot.loader 目录,也就是 Spring Boot 的 spring-boot-loader 工具模块,它就是 java -jar xxx.jar 启动 Spring Boot 项目的秘密所在,上面的 Main-Class 指定的就是该工具模块中的一个类

  4. https://www.jianshu.com/p/b04a54873f3b

MANIFEST.MF

META-INF/MANIFEST.MF 文件如下:

Manifest-Version: 1.0 Implementation-Title: spring-boot-study Implementation-Version: 1.0.0-SNAPSHOT Built-By: jingping Implementation-Vendor-Id: org.springframework.boot.demo Spring-Boot-Version: 2.0.3.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher # spring-boot-loader 中的启动类 Start-Class: org.springframework.boot.demo.Application # 你的 Spring Boot 项目中的启动类 Spring-Boot-Classes: BOOT-INF/classes/ # 你的 Spring Boot 项目编译后的 .class 文件所在目录 Spring-Boot-Lib: BOOT-INF/lib/ # 你的 Spring Boot 项目所引入的第三方依赖所在目录 Created-By: Apache Maven 3.6.3 Build-Jdk: 1.8.0_251 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-boot-starter-parent/info-dependencies/dwzq-info/info-stock-project/sp-provider

参考 Oracle 官方对该的说明:

  • Main-Class:Java 规定的 jar 包的启动类,这里设置为 spring-boot-loader 项目的 JarLauncher 类,进行 Spring Boot 应用的启动

  • Start-Class:Spring Boot 规定的启动类,这里通过 Spring Boot Maven Plugin 插件打包时,会设置为我们定义的 Application 启动类

为什么不直接将我们的 Application 启动类设置为 Main-Class 启动呢?

因为通过 Spring Boot Maven Plugin 插件打包后的 jar 包,我们的 .class 文件在 BOOT-INF/classes/ 目录下,在 Java 默认的 jar 包加载规则下找不到我们的 Application 启动类,也就需要通过 JarLauncher 启动加载。

当然,还有一个原因,Java 规定可执行器的 jar 包禁止嵌套其它 jar 包,在 BOOT-INF/lib 目录下有我们 Spring Boot 应用依赖的所有第三方 jar 包,因此spring-boot-loader 项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader,支持加载 BOOT-INF/classes 目录下的 .class 文件,以及 BOOT-INF/lib 目录下的 jar 包。

接下来,我们一起来看看 Spring Boot 的 JarLauncher 这个类

1. JarLauncher

类图:

上面的 WarLauncher 是针对 war 包的启动类,和 JarLauncher 差不多,感兴趣的可以看一看,这里我们直接来看到 JarLauncher 这个类

public class JarLauncher extends ExecutableArchiveLauncher { static final String BOOT_INF_CLASSES = “BOOT-INF/classes/”; static final String BOOT_INF_LIB = “BOOT-INF/lib/”; public JarLauncher() { } protected JarLauncher(Archive archive) { super(archive); } @Override protected boolean isNestedArchive(Archive.Entry entry) { // 只接受 `BOOT-INF/classes/` 目录 if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } // 只接受 `BOOT-INF/lib/` 目录下的 jar 包 return entry.getName().startsWith(BOOT_INF_LIB); } /** * 这里是 java -jar 启动 SpringBoot 打包后的 jar 包的入口 * 可查看 jar 包中的 META-INF/MANIFEST.MF 文件(该文件用于对 Java 应用进行配置) * 参考 Oracle 官方对于 jar 的说明(https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html) * 该文件其中会有一个配置项:Main-Class: org.springframework.boot.loader.JarLauncher * 这个配置表示会调用 JarLauncher#main(String[]) 方法,也就当前方法 */ public static void main(String[] args) throws Exception { // <1> 创建当前类的实例对象,会创建一个 Archive 对象(当前应用),可用于解析 jar 包(当前应用)中所有的信息 // <2> 调用其 launch(String[]) 方法 new JarLauncher().launch(args); } }

可以看到它有个 main(String[]) 方法,前面说到的 META-INF/MANIFEST.MF 文件中的 Main-Class 配置就是指向了这个类,也就会调用这里的 main 方法,会做下面两件事:

  1. 创建一个 JarLauncher 实例对象,在 ExecutableArchiveLauncher 父类中会做以下事情:

public abstract class ExecutableArchiveLauncher extends Launcher { private final Archive archive; public ExecutableArchiveLauncher() { try { // 为当前应用创建一个 Archive 对象,可用于解析 jar 包(当前应用)中所有的信息 this.archive = createArchive(); } catch (Exception ex) { throw new IllegalStateException(ex); } } protected final Archive createArchive() throws Exception { // 获取 jar 包(当前应用)所在的绝对路径 ProtectionDomain protectionDomain = getClass().getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; String path = (location != null) ? location.getSchemeSpecificPart() : null; if (path == null) { throw new IllegalStateException(“Unable to determine code source archive”); } // 当前 jar 包 File root = new File(path); if (!root.exists()) { throw new IllegalStateException("Unable to determine code source archive from " + root); } // 为当前 jar 包创建一个 JarFileArchive(根条目),需要通过它解析出 jar 包中的所有信息 // 如果是文件夹的话则创建 ExplodedArchive(根条目) return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root)); } }

会为当前应用创建一个 Archive 对象,可用于解析 jar 包(当前应用)中所有的信息,可以把它理解为一个“根”对象,可以通过它获取我们所需要的类信息

  1. 调用 JarLauncher#launch(String[]) 方法,也就是调用父类 Launcher 的这个方法

2. Launcher

org.springframework.boot.loader.Launcher,Spring Boot 应用的启动器

2. launch 方法

public abstract class Launcher { /** * Launch the application. This method is the initial entry point that should be * called by a subclass {@code public static void main(String[] args)} method. * @param args the incoming arguments * @throws Exception if the application fails to launch */ protected void launch(String[] args) throws Exception { // <1> 注册 URL(jar)协议的处理器 JarFile.registerUrlProtocolHandler(); // <2> 先从 `archive`(当前 jar 包应用)解析出所有的 JarFileArchive // <3> 创建 Spring Boot 自定义的 ClassLoader 类加载器,可加载当前 jar 中所有的类 ClassLoader classLoader = createClassLoader(getClassPathArchives()); // <4> 获取当前应用的启动类(你自己写的那个 main 方法) // <5> 执行你的那个 main 方法 launch(args, getMainClass(), classLoader); } }

会做以下几件事:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。

还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
在这里插入图片描述

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

ux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算**

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值