百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

SpringBoot打包部署解析:jar包的生成和结构

off999 2025-01-13 16:49 12 浏览 0 评论

SpringBoot打包部署解析

SpringBoot项目支持两种常见的打包形式: jar 包和 war 包。默认情况下创建的 Spring Boot项目是采用 jar 包形式,如果项目需要 war 包,可通过修改配置打成 war 包。

本章我们将围绕 jar 包和 war 包的运作原理及相关操作进行讲解。

SpringBoo的jar 包

Spring Boot 的 jar 包项目发布形式简单、快捷且内置 web 容器,因此 Spring Boot 将其作为默认选项。在享受便利的同时,我们也需要多少了解一下 Spring Boot 的 jar 包是如何生成的,以及如何通过 jar 包启动运行。本节从 jar 包的生成、结构、运作原理来分析 Spring Boot的实现。

jar包的生成

Spring Boot 的可执行 jar 包又称作 fat jar”,是包含所有三方依赖的 jar。它与传统 jar 包最大的不同是包含了一个 lib 目录和内嵌了 web 容器(以下均以 tomcat 为例)。

jar 包通 常是由集成在 pom.xml 文件中的 maven 插件来生成的。

配置在 pom 文件 build 元素中的 plugins 内。

<build>
<plugins>
<plugin>
<groupId>org . springframework. boot</groupId>
<artifactId>spring- boot-maven- plugin</ artifactId>
</plugin>
</plugins>
</build>

spring-boot-maven-plugin 项目存在于 spring-boot-tools 目录中。spring-boot-maven-plugin默认有 5 个 goals: repackage、 run、 start、 stop、 build-info。在打包的时候默认使用的是 repackage。

spring-boot-maven-plugin 的 repackage 能够将 mvn package 生成的软件包,再次打包为可执行的软件包,并将 mvn package 生成的软件包重命名为*.original。

这就为什么当执行 maven clean package 时,spring-boot-maven-plugin 会在 target 目录下生成两个 jar 文件。

spring - learn-0.0.1- SNAPSHOT. jar

spring - learn-0.0.1- SNAPSHOT . jar . original其中我们可以将 spring-learn-0.0. 1-SNAPSHOTjar.original 文件的后缀 original 去掉,生成的新jar包便是包含业务代码的包(普通的jar包) 。另外的spring-learn-0.0. 1-SNAPSHOTjar包则是在 Spring Boot 中通过 jar jar 启动的包,它包含了应用的依赖,以及 spring boot 相关 class。

spring-boot-maven-plugin 的 repackage 在代码层面调用了 RepackageMojo 的 execute 方法。RepackageMojo 类就是 提供重新打包现有的 jar 或 war 包文件,使得它们可以使用 javajar 来进行启动。

RepackageMojo 的 execute 方法如下。

@Override
public void execute() throws MojoExecut ionException, MojoFailureException
if (this. project . getPackaging() . equals("pom")) {
getLog() . debug("repackage goal could not be applied to pom project.");
return;
if (this.skip) {
getLog() . debug("skipping repackaging as per configuration.");
return;
repackage();
}

在 execute 方法中判断了是否为 pom 项目和是否跳过,如果是,则打印 debug 日志并返回;否则继续执行 repackage 方法。RepackageMojo 中的 repackage 方法相关源代码及操作解析如下。

private void repackage() throws MojoExecutionException {
// maven 生成的 jar, 最终的命名将加上. original 后缀
Artifact source = getSourceArtifact();
//最终为可执行 jar,即 fat jar
File target = getTargetFile();
//获取重新打包器,将 maven 生 成的 jar 重新打包成可执行 jar
Repackager repackager = getRepackager(source . getFile());
//查找并过滤项目运行时依赖的 jar
Set<Artifact> artifacts = filterDependenc ies(this . project. getArtifacts(),
getFilters(getAdditionalFilt
ers()));
//将 artifacts 转换成 L ibraries
Libraries libraries = new ArtifactsLibraries(artifacts, this . requiresUnpa
k,
getLog());ry {
/获得 Spring Boot 启动脚本
LaunchScript launchScript = getLaunchScript();
//执行重新打包,生成 fat jar
repackager . repackage(target, libraries, launchScript);
catch (IOException ex) {
throw new MojoExecut ionException(ex. getMessage(), ex); }
将 maven 生成的 jar 更新成 original 文件
updateArtifact(source, target, repackager . getBackupFile());
}

关于整个 repackage 方法的操作流程在上面代码中已经进行相应注释说明,其基本过程为:获得 maven 生成的普通 jar 包、获得目标 File 对象、获得重新打包器、获得依赖 jar 包、 获得启动脚本,最后通过重新打包器进行重新打包为可通过 java -jar 执行的 jar 包。

其中我们重点看获取 Repackager 的方法 getRepackager 的源代码。

private Repackager getRepackager(File source) {
Repackager repackager = new Repackager(source, this . layoutFactory);
repackager . addMainClassTimeoutWarningL istener(
new LoggingMainClassTimeoutWarningl istener());
//设置 main class 的名称,如果不指定, 则会查找第一个包含 main 方法的类
// repackage 最后将会设置 org. springframework . boot . Loader. JarLauncher
repackager . setMainClass(this . mainClass);
if (this.layout != null) {
getLog(). info("Layout: "+
this. layout);
//比如,layout 返@org. springframework. boot. loader. tools. Layouts . Jar
repackager . setLayout(this . layout. layout());
}
return repackager;
}

getRepackager 方法主要是根据将要被转换的文件(jar 或 war) 创建了 Repackager 对象,并设置启动用的 MainClass 为 org. springframework.boot.loader.JarLauncher,该配置对应于 jar 包中 Manifest.MF 文件内的 MainClass 值。

同时,如果 layout 不为 null, 通过内部枚举类 L ayoutType 提供的 layout 方法获取对应的重新打包的实现类,比如针对 jar 包的 org.springframework. boot.loader.tools.Layouts.Jar 类。

枚举类 LayoutType 的定义如下。

public enum LayoutType {
JAR(new Jar()),WAR(new War()),
ZIP(new Expanded()),
DIR(new Expanded()),
NONE(new None());
}

从 LayoutType 的定义可以看出,Spring Boot 其实是支持多种类型的 archive ( 即归档文件) : jar 类型、war 类型、zip 类型、 文件目录类型和 NONE。很显然,使用了相同的实现类来处理 ZIP 文件和 DIR 文件。

jar 类型为 Layouts 类的内部类,可以简单看一下 jar 类型的处理类都包含 了哪些内容。

public static class Jar implements RepackagingLayout {
//获取具体的 L ancher 类全路径
@Override
public String getLauncherClassName() {
return "org. springframework. boot . loader.Jarlauncher";
/获得具体的依赖 jar 包路径
@Override
public String getL ibraryDestination(String libraryName, LibraryScope scop
return "BOOT - INF/lib/";
//获取重新打包的 class 文件路径
@Override
public String getRepackagedClassesLocation() {
return "BOOT -INF/classes/";}
}

通过源代码可以看出,jar 类型的归档文件(jar 包) 中包含了 jar 包启动的 Main-class ( JarLauncher )BOOT-INF/lib/目录和 BOOT-INF/classes/目录。如果看 Expanded 和 None 类,会发现它们又继承自 jar。

最后,我们简单看一下 RepackageMojo 中的 repackage 调用所获取的 Repackager 的repackage 方法。Repackager 中 repackage 方法源码如下。

public void repackage(File destination, Libraries libraries, LaunchScript 1aunch
Script) throws IOException {
//校验目标文件
F (destination == null| | destination. isDirectory())
throw new illegalArgumentException("Invalid destination");
//校验依赖库f (libraries = null) {
throw new IllegalArgumentException("Libraries must not be nu1l");
//校验是否存在对应的 Layout, 如果不存在则创建
if (this.layout == null) {
this. layout = getL ayoutFactory() . getLayout(this . source);
destination = destination. getAbsoluteFile();
File workingSource = this. source;
//检查是否已经重新打包
if (alreadyRepackaged() && this. source.equals(destination)) {
//如果目标文件和 source 相同, 则删除原有备份文件( . original 结尾的), 重新备份 so
urce 文件
if (this. source . equals(destination)) {
workingSource = getBackupFile();
workingSource. delete();
renameFile(this. source, workingSource);
destination. delete();
try {
try (JarFile jarFileSource = new JarFile(workingSource)) {
//核心功能就是创建 JarWriter 向文件指定文件中写入内容
repackage(jarFileSource, destination, libraries, launchScript); }
finally {
if (!this . backupSource && !this. source . equals (workingSource)) {
deleteFile(workingSource);
}
}
}

上述代码的核心业务逻辑如下。

.校验各类参数(文件和路径是否存在)。

.备份待重新打包的文件以.original 结尾, 如果已经存在备份文件则先执行删除操作。

:生成目标文件之前,先清除一下目标文件。

调用重载的 repackage 方法,进行具体(jar 包)文件的生成和 MANIFESTMF 的信息写入。

.最后,判断并执行 workingSource 的清除操作。

用一句话总结上述过程:当符合条件时,对原有 jar 包文件进行备份,并生成新的可以通过 jar-jar 启动的文件。

关于重新打包的 jar 的目录结构及 MANIFEST.MF 文件中的信息,我们将在下一节进行讲解。

jar包的结构

在上一节中,通过 spring-boot-maven-plugin 生 成了可执行的 jar 包,下面分析-下 jar 包spring-learn-0.0.1-SNAPSHOT.jar的目录结构。

在上述结构中,BOOT-INF/classes 目录中存放业务代码,BOOT-INF/ib 目录中存放了除java 虚拟机之外的所有依赖; org 目 录中存放了 Spring Boot 用来启动 jar 包的相关 class文件; META-INF 目录中存放了 MANIFEST.MF、maven 信息和 spring factories 文件。

其中,Manifest.MF 文件通常被用来定义扩展或档案打包相关数据,它是一个元数据文件,数据格式为名/值对。一个可执行的 jar 文件需要通过该文件来指出该程序的主类。

Manifest-Version: 1.0
Implementation-Title: spring-learn
Implementation-Version: 0. 0.1-SNAPSHOT
Start-Class: com. secbro2. learn. SpringLearnApplication
Spring - Boot-Classes: B0OT-INF/classes/
Spring-Boot-Lib: B0OT-INF/lib/
Build-Jdk-Spec: 1.8
Spring- Boot -Version: 2.2.1. RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org. springframework . boot . loader .Jarlauncher

Manifest.MF 文件中定义 Main-Class 设置为org. springframework.boot.loader.JarLauncher, 也 就 是 说 , jar 包 程 序 启 动 入 口 为JarL .auncher 类的 main 方法。JarLauncher 类位 于 spring-boot-loader 项目中,在 jar 包的 org 目录中便存储着 Launcher 相关类的 class 文件。

项目的弓导类定义在 Start-Class 属性中,需要注意的是,Start-Class 属性并非 Java 标准的 Manifest.MF 属性。

本文给大家讲解的内容是SpringBoot打包部署解析:jar包的生成和结构

  1. 下篇文章给大家讲解的是SpringBoot打包部署解析:Launcher实现原理;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

相关推荐

让 Python 代码飙升330倍:从入门到精通的四种性能优化实践

花下猫语:性能优化是每个程序员的必修课,但你是否想过,除了更换算法,还有哪些“大招”?这篇文章堪称典范,它将一个普通的函数,通过四套组合拳,硬生生把性能提升了330倍!作者不仅展示了“术”,更传授...

7 段不到 50 行的 Python 脚本,解决 7 个真实麻烦:代码、场景与可复制

“本文整理自开发者AbdurRahman在Stackademic的真实记录,所有代码均经过最小化删减,确保在50行内即可运行。每段脚本都对应一个日常场景,拿来即用,无需额外依赖。一、在朋...

Python3.14:终于摆脱了GIL的限制

前言Python中最遭人诟病的设计之一就是GIL。GIL(全局解释器锁)是CPython的一个互斥锁,确保任何时刻只有一个线程可以执行Python字节码,这样可以避免多个线程同时操作内部数据结...

Python Web开发实战:3小时从零搭建个人博客

一、为什么选Python做Web开发?Python在Web领域的优势很突出:o开发快:Django、Flask这些框架把常用功能都封装好了,不用重复写代码,能快速把想法变成能用的产品o需求多:行业...

图解Python编程:从入门到精通系列教程(附全套速查表)

引言本系列教程展开讲解Python编程语言,Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,它也是互联网最热门的编程语言之一。Python生态丰富,库(模块)极其丰富,这使...

Python 并发编程实战:从基础到实战应用

并发编程是提升Python程序效率的关键技能,尤其在处理多任务场景时作用显著。本文将系统介绍Python中主流的并发实现方式,帮助你根据场景选择最优方案。一、多线程编程(threading)核...

吴恩达亲自授课,适合初学者的Python编程课程上线

吴恩达教授开新课了,还是亲自授课!今天,人工智能著名学者、斯坦福大学教授吴恩达在社交平台X上发帖介绍了一门新课程——AIPythonforBeginners,旨在从头开始讲授Python...

Python GUI 编程:tkinter 初学者入门指南——Ttk 小部件

在本文中,将介绍Tkinter.ttk主题小部件,是常规Tkinter小部件的升级版本。Tkinter有两种小部件:经典小部件、主题小部件。Tkinter于1991年推出了经典小部件,...

Python turtle模块编程实践教程

一、模块概述与核心概念1.1turtle模块简介定义:turtle是Python标准库中的2D绘图模块,基于Logo语言的海龟绘图理念实现。核心原理:坐标系系统:原点(0,0)位于画布中心X轴:向右...

Python 中的asyncio 编程入门示例-1

Python的asyncio库是用于编写并发代码的,它使用async/await语法。它为编写异步程序提供了基础,通过非阻塞调用高效处理I/O密集型操作,适用于涉及网络连接、文件I/O...

30天学会Python,开启编程新世界

在当今这个数字化无处不在的时代,Python凭借其精炼的语法架构、卓越的性能以及多元化的应用领域,稳坐编程语言排行榜的前列。无论是投身于数据分析、人工智能的探索,还是Web开发的构建,亦或是自动化办公...

Python基础知识(IO编程)

1.文件读写读写文件是Python语言最常见的IO操作。通过数据盘读写文件的功能都是由操作系统提供的,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个...

Python零基础到精通,这8个入门技巧让你少走弯路,7天速通编程!

Python学习就像玩积木,从最基础的块开始,一步步搭建出复杂的作品。我记得刚开始学Python时也是一头雾水,走了不少弯路。现在回头看,其实掌握几个核心概念,就能快速入门这门编程语言。来聊聊怎么用最...

一文带你了解Python Socket 编程

大家好,我是皮皮。前言Socket又称为套接字,它是所有网络通信的基础。网络通信其实就是进程间的通信,Socket主要是使用IP地址,协议,端口号来标识一个进程。端口号的范围为0~65535(用户端口...

Python-面向对象编程入门

面向对象编程是一种非常流行的编程范式(programmingparadigm),所谓编程范式就是程序设计的方法论,简单的说就是程序员对程序的认知和理解以及他们编写代码的方式。类和对象面向对象编程:把...

取消回复欢迎 发表评论: