Springboot 可执行 Jar 的格式

spring-boot-loader 模块让 Spring Boot 支持可执行的 jar 和 war 文件。如果使用 Maven 或 Gradle 插件,则会自动生成可执行的 jar,您通常不需要了解它们的工作原理。

如果您需要从不同的 build 系统创建可执行 jar,或者您只是对底层技术感到好奇,本附录提供了一些背景知识。

1. 嵌套 JAR

Java 没有提供任何标准的方法来加载嵌套的 jar 文件(jar 文件本身包含在另一个 jar 中)。如果您需要分发可以从命令行运行而无需解包的自包含应用程序,这可能会出现问题。如果您需要分发一个可以从命令行运行而不需要解包的自包含的应用程序,那么这可能会有问题。

为了解决这个问题,许多开发人员使用“shaded” jars。shaded jar 将所有 jar 中的所有类打包到一个超级 jar 中。它的问题在于,很难查看应用程序中实际使用了哪些库。如果在多个 jar 中使用相同的文件名(但内容不同),也会出现问题。Spring Boot 采用了不同的方法,让您实际上可以直接嵌套 jar。

1.1. 可执行 Jar 文件结构

Spring Boot Loader 兼容的 jar 文件应按以下方式构建:

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

应用程序类应放置在嵌套 的BOOT-INF/classes 目录中。依赖项应该放在嵌套的 BOOT-INF/lib 目录中。

1.2. 可执行 War 文件结构

Spring Boot Loader 兼容的 war 文件应按以下方式构建:

example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar

依赖项应该放在嵌套的 WEB-INF/lib 目录中。运行嵌入式时需要但部署到传统 Web 容器时不需要的任何依赖项都应放在WEB-INF/lib-provided.

1.3. 索引文件

Spring Boot Loader 兼容的 jar 和 war 档案可以在BOOT-INF/目录下包含额外的索引文件。 jars 和 wars 都可以有 classpath.idx 文件,它提供了 jars 应该被添加到 classpath 的顺序。layers.idx 文件只能用于 jar,它允许将 jar 拆分为逻辑层以创建 Docker/OCI 映像。

索引文件遵循兼容 YAML 的语法,以便第三方工具可以轻松解析它们。但是,在内部这些文件不会被解析为 YAML,它们必须完全按照下面描述的格式编写才能使用。

1.4. Classpath 索引

classpath 索引文件可以在 BOOT-INF/classpath.idx, 它提供了一个 jar 名称列表(包括目录),按照它们应该被添加到 classpath 中的顺序。每行必须以破折号空格 ( "-·") 开头,名称必须用双引号括起来。

例如,给定以下 jar:

example.jar
 |
 +-META-INF
 |  +-...
 +-BOOT-INF
    +-classes
    |  +...
    +-lib
       +-dependency1.jar
       +-dependency2.jar

索引文件如下所示:

- "BOOT-INF/lib/dependency2.jar"
- "BOOT-INF/lib/dependency1.jar"

1.5. Layer 索引

Layer 索引文件可以在 BOOT-INF/layers.idx。 它提供了一个层列表以及应该包含在其中的 jar 部分。层是按照应该添加到 Docker/OCI 镜像的顺序编写的。Layer 名称是带双引号的字符串,前缀为破折号空格 ( "-·") ,后缀为冒号 ( ":") 。Layer 内容是一个文件名或目录名,是以空格空格破折号空格( "··-·")作为前缀的带双引号的字符串 。目录名以 / 结尾,文件名不用。当使用目录名称时,意味着该目录中的所有文件都在同一层中。

Layer 索引的典型示例是:

- "dependencies":
  - "BOOT-INF/lib/dependency1.jar"
  - "BOOT-INF/lib/dependency2.jar"
- "application":
  - "BOOT-INF/classes/"
  - "META-INF/"

2. Spring Boot 的 JarFile 类

用于支持加载嵌套 jar 的核心类是 org.springframework.boot.loader.jar.JarFile. 它允许您从标准 jar 文件或嵌套的子 jar 数据中加载 jar 的内容。首次加载时,每个 JarEntry 的位置都被映射到外部 jar 的物理文件偏移量,如下例所示:

我的应用程序
+--------------------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+---------+|
|| A.class ||| B.class | C.class ||
|+-----------------+||+-----------+---------+|
+--------------------------------+-------------------------+
 ^ ^ ^
 0063 3452 3980

上面的示例显示了如何在 myapp.jar/BOOT-INF/classes 中找到位置为 0063A.class。 嵌套 jar 的 B.class 实际上可以在 myapp.jar3452 位置处找到,C.class3980 处。

有了这些信息,我们就可以通过寻找外部 jar 的适当部分来加载特定的嵌套条目。我们不需要解压存档,也不需要将所有条目数据读入内存。

2.1. 与标准 Java JarFile 的兼容性

Spring Boot Loader 努力保持与现有代码和库的兼容。 org.springframework.boot.loader.jar.JarFile 继承自 java.util.jar.JarFile 并应作为替代品。getURL() 方法返回一个 URL ,其打开一个与 java.net.JarURLConnection 兼容的连接,并可与 Java 的URLClassLoader 一起使用。

3. 启动可执行的 jars

org.springframework.boot.loader.Launcher 是作为一个可执行 JAR 的主入口点一个特殊的启动类。它是 jar 文件中的实际 Main-Class,用于设置适当的URLClassLoader 并最终调用您的 main() 方法。

有三个 Launcher 子类(JarLauncherWarLauncher,和 PropertiesLauncher)。它们的目的是从嵌套的 jar 文件或目录中的 war 文件中加载资源(.class文件等),而不是显式地在 classpath 上加载资源。在JarLauncherWarLauncher 的情况下,嵌套路径是固定的。

  • JarLauncher :在 BOOT-INF/lib/ 中查找。

  • WarLauncherWEB-INF/lib/WEB-INF/lib-provided/ 查找。

  • PropertiesLauncher:默认情况下,在 BOOT-INF/lib/ 中查找。您可以添加其他位置,通过设置名为 LOADER_PATH 的环境变量,或在 loader.properties 文件中设置loader.path 属性 (这是一个以逗号分隔的目录、档案或档案中的目录的列表)。

3.1. Launcher Manifest

您需要指定一个适当的Launcher作为 的 META-INF/MANIFEST.MF 文件的 Main-Class 属性。您要启动的实际类(即包含main方法的应用程序类)应在 Start-Class 属性中指定。

以下示例显示了一个典型的可执行 jar 文件的 MANIFEST.MF

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

对于 war 文件,它将如下所示:

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication

4. PropertiesLauncher 功能

PropertiesLauncher 有一些可以通过外部属性(系统属性、环境变量、manifest entries 或 loader.properties文件)启用的特殊功能。下表描述了这些属性:

KeyPurpose
loader.path逗号分隔的类路径,例如lib,${HOME}/app/lib。 前面的条目优先,类似于 javac 命令行 的-classpath
loader.home用于解析loader.path 中的相对路径。 例如,给定loader.path=lib,则 ${loader.home}/lib是一个 classpath 位置(以及该目录中的所有 jar 文件)。此属性也用于定位 loader.properties 文件,如下例中/opt/app默认为${user.dir}
loader.argsmain 方法的默认参数(空格分隔)。
loader.main要启动的主类的名称(例如,com.app.Application)。
loader.config.name属性文件的名称(例如,launcher)。默认为 loader
loader.config.location属性文件的路径(例如,classpath:loader.properties)。默认为 loader.properties
loader.system布尔标志,指示应将所有属性添加到系统属性。默认为false

当指定为环境变量或 manifest entries 时,应使用以下名称:

KeyManifest entryEnvironment variable
loader.pathLoader-PathLOADER_PATH
loader.homeLoader-HomeLOADER_HOME
loader.argsLoader-ArgsLOADER_ARGS
loader.mainStart-ClassLOADER_MAIN
loader.config.locationLoader-Config-LocationLOADER_CONFIG_LOCATION
loader.systemLoader-SystemLOADER_SYSTEM

Tip

构建插件会在构建 fat jar 时自动将 Main-Class 属性移动到 Start-Class。如果使用它,请使用 Main-Class 属性指定要启动的类的名称并省略 Start-Class

以下规则适用于使用 PropertiesLauncher

  • 首先在 loader.home 中搜索 loader.properties,然后在 classpath 的根中搜索,然后在 classpath:/BOOT-INF/classes 中。 使用最先找到的文件。
  • loader.home 是仅当 loader.config.location 未指定时附加属性文件的目录位置(覆盖默认值)。
  • loader.path 可以包含目录(对 jar 和 zip 文件进行递归扫描)、archive 路径、archive 中扫描 jar 文件的目录(例如,dependencies.jar!/lib)或通配符模式(对于默认的 JVM 行为)。archive 路径可以相对于loader.home。或者带有jar:file:前缀的文件系统中的任何位置。
  • loader.path(如果为空)默认为BOOT-INF/lib(意味着本地目录,或嵌套目录,如果从存档运行)。因此,未提供其他配置的 PropertiesLauncher 的行为与 JarLauncher 的行为相同。
  • loader.path 不能用于配置 loader.properties 的位置(用于搜索后者的 classpath 是 PropertiesLauncher 启动时的 JVM classpath )。
  • 在使用所有值之前,从系统和环境变量加上属性文件本身进行占位符替换。
  • 属性的搜索顺序(在多个地方查看是有意义的)是环境变量、系统属性、loader.properties、分解的 archive manifest 和 archive manifest。

可执行 Jar 的限制

在使用 Spring Boot Loader 打包的应用程序时,您需要考虑以下限制:

  • Zip entry compression:必须使用 ZipEntry.STORED 方法保存嵌套 jar 的 ZipEntry。这是必需的,以便我们可以直接查找嵌套 jar 中的单个内容。嵌套 jar 文件本身的内容仍然可以压缩,就像外部 jar 中的任何其他条目一样。

  • System classLoader:启动的应用程序在加载类时应该使用 Thread.getContextClassLoader()(大多数库和框架默认是这样做的)。尝试使用 ClassLoader.getSystemClassLoader() 加载嵌套的 jar 会失败。 java.util.Logging 始终使用系统类加载器。因此,您应该考虑不同的日志记录实现。

6. 其他的单个 Jar 的解决方案

如果上述限制意味着您无法使用 Spring Boot Loader,请考虑以下替代方案:

原文

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值