可执行 Jar

1. 嵌套 JAR

Java 不提供任何标准方法来加载嵌套的 jar 文件(即本身包含在 jar 中的 jar 文件)。 如果您需要分发一个独立的应用程序,该应用程序可以从命令行运行而无需解包,则这可能会出现问题。spring-doc.cadn.net.cn

为了解决这个问题,许多开发人员使用“阴影”罐子。 阴影 jar 将所有 jar 中的所有类打包到一个“uber jar”中。 着色 jar 的问题在于很难看到应用程序中实际存在哪些库。 如果在多个 jar 中使用相同的文件名(但内容不同),也可能会出现问题。 Spring Boot 采用了不同的方法,允许您直接嵌套 jar。spring-doc.cadn.net.cn

1.1. 可执行的 jar 文件结构

与 Spring Boot Loader 兼容的 jar 文件应按以下方式构建:spring-doc.cadn.net.cn

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目录。spring-doc.cadn.net.cn

1.2. 可执行的战争文件结构

与 Spring Boot Loader 兼容的 war 文件应按以下方式构建:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

1.3. 索引文件

与 Spring Boot Loader 兼容的 jar 和 war 存档可以在BOOT-INF/目录。 一个classpath.idxfile 可以为 jar 和 war 提供,它提供了将 jar 添加到类路径的顺序。 这layers.idx文件只能用于 jar,它允许将 jar 拆分为逻辑层以创建 Docker/OCI 映像。spring-doc.cadn.net.cn

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

1.4. 类路径索引

类路径索引文件可以在BOOT-INF/classpath.idx. 通常,它是由 Spring Boot 的 Maven 和 Gradle 构建插件自动生成的。 它提供了一个 jar 名称(包括目录)的列表,按它们应该添加到类路径的顺序排列。 当由构建插件生成时,此类路径顺序与构建系统用于运行和测试应用程序的顺序匹配。 每行必须以虚线空格 () 开头,并且名称必须用双引号括起来。"-·"spring-doc.cadn.net.cn

例如,给定以下 jar:spring-doc.cadn.net.cn

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

索引文件将如下所示:spring-doc.cadn.net.cn

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

1.5. 层索引

图层索引文件可以在BOOT-INF/layers.idx. 它提供了层的列表以及应包含在其中的罐子部分。 层按照应添加到 Docker/OCI 映像的顺序编写。 图层名称写为带引号的字符串,前缀为虚线空格 () 和冒号 ("-·"":") 后缀。 图层内容是文件或目录名称,以空格短划线 () 为前缀的带引号的字符串。 目录名称以 结尾,文件名则不然。 当使用目录名称时,这意味着该目录中的所有文件都在同一层中。"··-·"/spring-doc.cadn.net.cn

图层索引的一个典型示例是:spring-doc.cadn.net.cn

- "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 的物理文件偏移量,如以下示例所示:spring-doc.cadn.net.cn

myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
||     A.class      |||  B.class  |  C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
 ^                    ^           ^
 0063                 3452        3980

前面的示例演示了如何A.class可以在/BOOT-INF/classesmyapp.jar在位置0063.B.class从嵌套的 jar 实际上可以在myapp.jar在位置3452C.class在位置3980.spring-doc.cadn.net.cn

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

2.1. 与标准 Java“JarFile”的兼容性

Spring Boot Loader 努力与现有代码和库保持兼容。org.springframework.boot.loader.jar.JarFilejava.util.jar.JarFile并且应该可以作为直接替代品。 这getURL()方法返回一个URL打开兼容的连接java.net.JarURLConnection并且可以与 Java 的URLClassLoader.spring-doc.cadn.net.cn

3. 启动可执行罐

org.springframework.boot.loader.Launcherclass 是一个特殊的引导类,用作可执行 jar 的主要入口点。 这是实际的Main-Class,它用于设置适当的URLClassLoader并最终调用您的main()方法。spring-doc.cadn.net.cn

有三个Starters子类(JarLauncher,WarLauncherPropertiesLauncher). 它们的目的是加载资源 (.class文件等)来自目录中的嵌套 jar 文件或 war 文件(而不是类路径上显式的那些文件)。 在以下情况下JarLauncherWarLauncher,嵌套路径是固定的。JarLauncherBOOT-INF/lib/WarLauncherWEB-INF/lib/WEB-INF/lib-provided/. 如果您想要更多,您可以在这些位置添加额外的罐子。 这PropertiesLauncherBOOT-INF/lib/默认情况下,在您的应用程序存档中。 您可以通过设置名为LOADER_PATHloader.pathloader.properties(这是目录、档案或档案中的目录的逗号分隔列表)。spring-doc.cadn.net.cn

3.1. Starters清单

您需要指定适当的Launcher作为Main-Class属性META-INF/MANIFEST.MF. 您要启动的实际类(即包含main方法)应在Start-Class属性。spring-doc.cadn.net.cn

以下示例显示了典型的MANIFEST.MF对于可执行的 jar 文件:spring-doc.cadn.net.cn

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

对于战争文件,如下所示:spring-doc.cadn.net.cn

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication
您无需指定Class-Path清单文件中的条目。 类路径是从嵌套的 jar 中推断出来的。

4. PropertiesLauncher 功能

PropertiesLauncher具有一些可以使用外部属性(系统属性、环境变量、清单条目或loader.properties). 下表描述了这些属性:spring-doc.cadn.net.cn

钥匙 目的

loader.pathspring-doc.cadn.net.cn

逗号分隔的类路径,例如lib,${HOME}/app/lib. 较早的条目优先,就像常规条目一样-classpathjavac命令行。spring-doc.cadn.net.cn

loader.homespring-doc.cadn.net.cn

用于解析loader.path. 例如,给定loader.path=lib然后${loader.home}/lib是一个类路径位置(以及该目录中的所有 jar 文件)。 此属性还用于查找loader.properties文件,如以下示例所示/opt/app它默认为${user.dir}.spring-doc.cadn.net.cn

loader.argsspring-doc.cadn.net.cn

main 方法的默认参数(空格分隔)。spring-doc.cadn.net.cn

loader.mainspring-doc.cadn.net.cn

要启动的主类的名称(例如com.app.Application).spring-doc.cadn.net.cn

loader.config.namespring-doc.cadn.net.cn

属性文件的名称(例如launcher). 它默认为loader.spring-doc.cadn.net.cn

loader.config.locationspring-doc.cadn.net.cn

属性文件的路径(例如classpath:loader.properties). 它默认为loader.properties.spring-doc.cadn.net.cn

loader.systemspring-doc.cadn.net.cn

布尔标志,指示应将所有属性添加到系统属性。 它默认为false.spring-doc.cadn.net.cn

当指定为环境变量或清单条目时,应使用以下名称:spring-doc.cadn.net.cn

钥匙 清单条目 环境变量

loader.pathspring-doc.cadn.net.cn

Loader-Pathspring-doc.cadn.net.cn

LOADER_PATHspring-doc.cadn.net.cn

loader.homespring-doc.cadn.net.cn

Loader-Homespring-doc.cadn.net.cn

LOADER_HOMEspring-doc.cadn.net.cn

loader.argsspring-doc.cadn.net.cn

Loader-Argsspring-doc.cadn.net.cn

LOADER_ARGSspring-doc.cadn.net.cn

loader.mainspring-doc.cadn.net.cn

Start-Classspring-doc.cadn.net.cn

LOADER_MAINspring-doc.cadn.net.cn

loader.config.locationspring-doc.cadn.net.cn

Loader-Config-Locationspring-doc.cadn.net.cn

LOADER_CONFIG_LOCATIONspring-doc.cadn.net.cn

loader.systemspring-doc.cadn.net.cn

Loader-Systemspring-doc.cadn.net.cn

LOADER_SYSTEMspring-doc.cadn.net.cn

构建插件会自动移动Main-Class属性设置为Start-Class当胖罐子建成时。 如果使用它,请使用Main-Class属性并省略Start-Class.

以下规则适用于使用PropertiesLauncher:spring-doc.cadn.net.cn

  • loader.propertiesloader.home,然后在类路径的根目录中,然后在classpath:/BOOT-INF/classes. 使用存在具有该名称的文件的第一个位置。spring-doc.cadn.net.cn

  • loader.home是附加属性文件的目录位置(覆盖默认值),仅当loader.config.location未指定。spring-doc.cadn.net.cn

  • loader.path可以包含目录(以递归方式扫描 jar 和 zip 文件)、归档路径、存档中扫描 jar 文件的目录(例如,dependencies.jar!/lib)或通配符模式(对于默认的 JVM 行为)。 存档路径可以相对于loader.home或文件系统中的任何位置,使用jar:file:前缀。spring-doc.cadn.net.cn

  • loader.path(如果为空)默认为BOOT-INF/lib(如果从存档运行,则表示本地目录或嵌套目录)。 正因为如此,PropertiesLauncher行为与JarLauncher当未提供其他配置时。spring-doc.cadn.net.cn

  • loader.path不能用于配置loader.properties(用于搜索后者的类路径是 JVM 类路径,当PropertiesLauncher已启动)。spring-doc.cadn.net.cn

  • 占位符替换是从系统和环境变量以及使用前所有值的属性文件本身完成的。spring-doc.cadn.net.cn

  • 属性的搜索顺序(在多个位置查找是有意义的)是环境变量、系统属性、loader.properties、分解的存档清单和存档清单。spring-doc.cadn.net.cn

5. 可执行 Jar 限制

使用 Spring Boot Loader 打包应用程序时,您需要考虑以下限制:spring-doc.cadn.net.cn

  • Zip 条目压缩: 这ZipEntry对于嵌套的 jar 必须使用ZipEntry.STORED方法。 这是必需的,以便我们可以直接查找嵌套 jar 中的单个内容。 嵌套 jar 文件本身的内容仍然可以压缩,外部 jar 中的任何其他条目也可以压缩。spring-doc.cadn.net.cn

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

6. 替代单罐解决方案