【七】SpringBoot为什么可以打成 jar包启动
SpringBoot为什么可以打成 jar包启动

简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一下了,不至于下次想不来了又去翻资料。
一、SpringBoot生成的jar包是什么
Spring Boot的可执行jar包又称作“fat jar”,那什么是fat jar呢?在java中,将应用程序及其依赖jar一起打包到一个独立的jar中,就叫fat jar,它也叫uberJar。springboot的打包方式就是这样,将应用程序代码打包到BOOT-INF.classes,将依赖包打包到BOOT-INF.lib目录,这里我们以xxl-job-admin-2.4.0-SNAPSHOT.jar为例来做说明,我们使用反编译工具jd将jar打开,目录如下:

各目录存放内容如下:
BOOT-INF/classes:目录存放应用编译后的class文件。
BOOT-INF/lib:目录存放应用依赖的第三方JAR包文件。
META-INF:目录存放应用打包信息(Maven坐标、pom文件)和MANIFEST.MF文件。
org:目录存放SpringBoot相关class文件。
这里我们首先关注一下配置文件:MANIFEST.MF,内容如下:
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Archiver-Version: Plexus Archiver
Built-By: user
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.xxl.job.admin.XxlJobAdminApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.6.7
Created-By: Apache Maven 3.6.1
Build-Jdk: 1.8.0_211
Main-Class: org.springframework.boot.loader.JarLauncher
参考 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 包。
二、JarLauncher启动器实现原理
上文描述了Application 的Main-Clas启动类是JarLauncher 类,那么接下来我们一起来看看 Spring Boot 的 JarLauncher 这个类
JarLauncher的继承关系如下:

JarLauncher全路径是org.springframework.boot.loader.JarLauncher。
public class JarLauncher extends ExecutableArchiveLauncher {
private static final String DEFAULT_CLASSPATH_INDEX_LOCATION = "BOOT-INF/classpath.idx";
static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> {
if (entry.isDirectory()) {
return entry.getName().equals("BOOT-INF/classes/");
}
return entry.getName().startsWith("BOOT-INF/lib/");
};
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
@Override
protected ClassPathIndexFile getClassPathIndex(Archive archive) throws IOException {
// Only needed for exploded archives, regular ones already have a defined order
if (archive instanceof ExplodedArchive) {
String location = getClassPathIndexFileLocation(archive);
return ClassPathIndexFile.loadIfPossible(archive.getUrl(), location);
}
return super.getClassPathIndex(archive);
}
private String getClassPathIndexFileLocation(Archive archive) throws IOException {
Manifest manifest = archive.getManifest();
Attributes attributes = (manifest != null) ? manifest.getMainAttributes() : null;
String location = (attributes != null) ? attributes.getValue(BOOT_CLASSPATH_INDEX_ATTRIBUTE) : null;
return (location != null) ? location : DEFAULT_CLASSPATH_INDEX_LOCATION;
}
@Override
protected boolean isPostProcessingClassPathArchives() {
return false;
}
@Override
protected boolean isSearchCandidate(Archive.Entry entry) {
return entry.getName().startsWith("BOOT-INF/");
}
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry);
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
通过new一个JarLauncher().launch(args)方式进行启动。
public class Jarlauncher{
...
private static final String JAR_MODE_LAUNCHER = "org.springframework.boot.loader.jarmode.JarModeLauncher";
...
protected void launch(String[] args) throws Exception {
//判断是否以一个分解模式的方式运行,如果是则运行,否则只支持规范的jar文件从而选择跳过
if (!isExploded()) {
JarFile.registerUrlProtocolHandler();
}
//获取类加载器
ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
//获取系统中的jar模型
String jarMode = System.getProperty("jarmode");
//加载启动引导类
String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER :
getMainClass();
//启动应用
launch(args, launchClass, classLoader);
}
}
相关文章:
【七】SpringBoot为什么可以打成 jar包启动
SpringBoot为什么可以打成 jar包启动 简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一…...
031-第三代软件开发-屏幕保护
第三代软件开发-屏幕保护 文章目录 第三代软件开发-屏幕保护项目介绍屏幕保护 关键字: Qt、 Qml、 MediaPlayer、 VideoOutput、 function 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Object Language&#…...
Ubuntu 22.04 更新完内核重启卡在 grub 命令行解决办法
倒霉伊始 升级内核过程中出现如下警告,然后重启引导失败: Warning: os-prober will not be executed to detect other bootable partitions 屏幕内容如下: GNU GRUB version 2.06Minimal BASH-like line editing is supported. For the fir…...
Stream流式处理
Stream流式处理: 建立在Lambda表达式基础上的多数据处理技术。 可以对集合进行迭代、去重、筛选、排序、聚合等处理,极大的简化了代码量。 Stream常用方法 Stream流对象的五种创建方式 //基于数组 String[] arr {"a","b","c…...
ROG STRIX GS-AX5400 使用笔记
1. 技术支持 咨询京东(因为是在京东购买的) 2. 增强信号设置 Note:关于设置的具体步骤,请参考教程《华硕路由器地区如何改成澳大利亚》。 操作路径:打开主页<192.168.50.1> ⇒ 输入用户名和密码后选择登录 ⇒ …...
【刷题-PTA】堆栈模拟队列(代码+动态图解)
【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q。 …...
FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法
正常代码,部署到server 2012时,在上传音频mp3文件时,显示错误“不支持给定路径的格式”,上传控件使用FileUpload控件: 因为程序之前是正常的,因此应该不是程序的问题。 上传时,发现在选择文件时…...
这两天的一些碎碎念
一直以来我都不算是一个非常热爱运维岗位的一个人,其实本行工作对于我来说只是一个工作。运维的广度很大,说什么工作了7年了,可最终总感觉还曾是一窍不通。 什么shell啊,什么python啊,什么大数据啊,7年里&a…...
Unity 最新DOTS系列之《Baking与Baker的详解》
Unity DOTS Baking与Baker详解 最近DOTS终于发布了正式的版本, 我们来分享一下DOTS里面Baking 与Baker的关键概念,方便大家上手学习掌握Unity DOTS开发。 对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀&…...
【Tensorflow 2.12 简单智能商城商品推荐系统搭建】
Tensorflow 2.12 简单智能商城商品推荐系统搭建 前言架构数据召回排序部署调用结尾 前言 基于 Tensorflow 2.12 搭建一个简单的智能商城商品推荐系统demo~ 主要包含6个部分,首先是简单介绍系统架构,接着是训练数据收集、处理,然后是召回模型、…...
Unity 单例-接口模式
单例-接口模式 使用接口方式实现的单例可以继承其它类,更加方便 using System.Collections; using System.Collections.Generic; using UniRx; using UniRx.Triggers; using UnityEngine; namespace ZYF {public interface ISingleton<TMono> where TMono : M…...
【Java 进阶篇】Java XML解析:从入门到精通
XML(可扩展标记语言)是一种常用的数据格式,用于存储和交换数据。在Java中,XML解析是一项重要的任务,它允许您从XML文档中提取和操作数据。本篇博客将从基础开始,详细介绍如何在Java中解析XML文档࿰…...
【图像配准】Canny边缘检测+模板配准红外可见光双路数据
研究目的 最近在做无人机遥感红外和可见光双路数据配准,由于红外相机视野范围较小,因此配准的目的主要是在可见光的视野范围内,裁剪出红外图像对应的部分,同时,保持可见光的高分辨率不变。 本文思路 本文尝试使用Ca…...
关于单机流程编排技术——docker compose安装使用的问题
最近在学习docker相关的东西,当我在docker上部署了一个nest应用,其中该应用中依赖了一个基于mysql镜像的容器,一个基于redis镜像的容器。那我,当我进行部署上线时,在启动nest容器时,必须保证redis容器和mys…...
Google Chrome的新“IP保护”功能将隐藏用户的IP地址
导语:在保护用户隐私方面,Google Chrome正在测试一项名为“IP保护”的新功能。通过使用代理服务器掩盖用户的IP地址,这项功能能够增强用户的隐私保护。在意识到IP地址可能被用于秘密追踪后,Google希望在确保用户隐私的同时&#x…...
做机器视觉工程师,苏州德创能不能去工作?
每一家公司都有自身特点,同时也每一家都有自身的bug。 苏州德创作为美国康耐视Cognex产品在华东最大的代理商,也是康耐视外包团队。那么苏州德创有哪些业务构成,业务的构成也是其招聘的主要人员的方向。 设备视觉供应商,如卓越&…...
交换机基础(二):VLAN 基础知识
一、VLAN 基础知识 虚拟局域网 (Virtual Local Area Network,VLAN) 是一种将局域网设 备从逻辑上划分成一个个网段,从而实现虚拟工作组的数据交换技术。 这一技术主要应用于3层交换机和路由器中,但主流应用还是在3层交换机中。 VLAN 是基于物理网络上构建…...
一个基于Vue3搭建的低代码数据可视化开发平台
JNPF是一个Vue3搭建的低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可完成业务需求。 在JNPF中,至少包含表单建模、流程设计、报表可视化、代码生成器、系统管理、前端UI等组件,这种情况下我们避免了重…...
经验风险最小化与结构风险最小化:优化机器学习模型的两种方法
随着大数据时代的到来,机器学习在各个领域中的应用越来越广泛。然而,在构建机器学习模型时,我们面临着两个主要的挑战:经验风险最小化和结构风险最小化。本文将深入探讨这两种方法,并分析它们在优化机器学习模型中的作…...
Java泛型中的问号是什么意思
通配符概念 因为 List 是泛型类,为了 表示各种泛型 List 的父类,可以使用类型通配符,类型通配符使用问号(?)表示,将一个问号当做类型元素传递个 List,可以表示为 List<?>,意思是 元素类型未知的 List…...
救命!这些毕设太好抄了,3000+毕设案例推荐第1022期
221、基于Java的环境保护在线监管智慧管理系统的设计与实现(论文+代码+PPT) 环境保护在线监管智慧管理系统主要功能包括:企业管理、监测点管理、污染物管理、污染源管理、水污染监测数据、大气污染监测数据、噪声污染监测数据、土壤污染监测…...
大学生福音!免费源码网搞定毕设:会员源码网深度解析
在大学的象牙塔里,毕业设计是每个计算机相关专业学生都要跨越的一道坎。从选题到实现,每一步都充满挑战,尤其是对于编程经验尚浅的同学来说,从零开始构建一个完整的系统更是难上加难。今天,就为大家介绍一个能让毕设之…...
Universal Debug Library:嵌入式双通道调试框架
1. Universal Debug Library 深度解析:面向嵌入式开发的双通道调试框架在嵌入式系统开发中,调试信息输出是贯穿硬件验证、固件调试、现场问题定位全生命周期的核心能力。传统Serial.print()系统存在明显局限:输出通道单一、格式化能力弱、平台…...
HiveWE:革新性魔兽争霸III地图编辑器,重新定义创作效率
HiveWE:革新性魔兽争霸III地图编辑器,重新定义创作效率 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 当你在制作魔兽争霸III地图时,是否曾因原版编辑器的加载速度缓慢而…...
CodeActAgent:以Python代码为通用动作空间,解锁LLM智能体复杂任务处理新范式
1. 为什么Python代码能成为LLM智能体的最佳动作空间? 当你第一次听说"用Python代码作为LLM智能体的动作空间"时,可能会觉得这个想法有点抽象。但想象一下,你正在教一个刚学编程的朋友完成数据分析任务。如果让他用自然语言描述每个…...
亚马逊德国站VAT发票自动筛选:手把手教你用浏览器控制台JS代码搞定(附Edge/Chrome/Firefox全版本)
亚马逊德国站VAT发票智能筛选:浏览器控制台JS代码实战指南 每次月底处理税务发票时,跨境电商卖家们是否总被海量的PDF文件淹没?特别是亚马逊德国站的卖家,面对后台密密麻麻的发票列表,手动筛选符合特定税号条件的文件不…...
NVIDIA Profile Inspector完整指南:解锁显卡隐藏性能的终极免费工具
NVIDIA Profile Inspector完整指南:解锁显卡隐藏性能的终极免费工具 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 还在为游戏卡顿、画面撕裂而烦恼吗?NVIDIA Profile Inspecto…...
RBF 神经网络车速预测模型功能说明书
基于RBF神经网络车速预测模型,根据历史车速信息,预测未来预测时域内的车速信息的时序预测模型,根据预测的信息对车辆进行控制可以对混动汽车的能量管理具有一定的参考意义 1.文件包括,训练工况(.mat数据,工…...
体验ai辅助开发:在快马平台与ai协作构建智能任务管理应用
最近尝试用AI辅助开发了一个任务管理应用,整个过程就像有个经验丰富的编程伙伴在旁边随时提供建议。在InsCode(快马)平台上,这种协作体验特别流畅,分享下具体实现过程: 初始框架搭建 输入"创建一个Vue3任务列表应用ÿ…...
告别浏览器!3分钟快速掌握Transmission Remote GUI远程下载管理终极方案
告别浏览器!3分钟快速掌握Transmission Remote GUI远程下载管理终极方案 【免费下载链接】transgui 🧲 A feature rich cross platform Transmission BitTorrent client. Faster and has more functionality than the built-in web GUI. 项目地址: htt…...
