-
BOOT-INF
目录,里面保存了我们自己 Spring Boot 项目编译后的所有文件,其中classes
目录下面就是编译后的 .class 文件,包括项目中的配置文件等,lib
目录下就是我们引入的第三方依赖 -
META-INF
目录,通过MANIFEST.MF
文件提供jar
包的元数据,声明jar
的启动类等信息。每个 Javajar
包应该是都有这个文件的,参考 Oracle 官方对于jar
的说明,里面有一个Main-Class
配置用于指定启动类 -
org.springframework.boot.loader
目录,也就是 Spring Boot 的spring-boot-loader
工具模块,它就是java -jar xxx.jar
启动 Spring Boot 项目的秘密所在,上面的Main-Class
指定的就是该工具模块中的一个类
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
方法,会做下面两件事:
- 创建一个
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
包(当前应用)中所有的信息,可以把它理解为一个“根”对象,可以通过它获取我们所需要的类信息
- 调用
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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
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、安卓逆向、云计算**