可执行 Jar
1. 嵌套 JAR
Java 不提供任何标准方法来加载嵌套的 jar 文件(即本身包含在 jar 中的 jar 文件)。 如果您需要分发一个独立的应用程序,该应用程序可以从命令行运行而无需解包,则这可能会出现问题。
为了解决这个问题,许多开发人员使用“阴影”罐子。 阴影 jar 将所有 jar 中的所有类打包到一个“uber 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. 可执行的战争文件结构
与 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/
目录。
一个classpath.idx
file 可以为 jar 和 war 提供,它提供了将 jar 添加到类路径的顺序。
这layers.idx
文件只能用于 jar,它允许将 jar 拆分为逻辑层以创建 Docker/OCI 映像。
索引文件遵循 YAML 兼容语法,以便第三方工具可以轻松解析它们。 但是,这些文件不会在内部解析为 YAML,它们必须完全按照下面描述的格式编写才能使用。
1.4. 类路径索引
类路径索引文件可以在BOOT-INF/classpath.idx
.
通常,它是由 Spring Boot 的 Maven 和 Gradle 构建插件自动生成的。
它提供了一个 jar 名称(包括目录)的列表,按它们应该添加到类路径的顺序排列。
当由构建插件生成时,此类路径顺序与构建系统用于运行和测试应用程序的顺序匹配。
每行必须以虚线空格 () 开头,并且名称必须用双引号括起来。"-·"
例如,给定以下 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. 层索引
图层索引文件可以在BOOT-INF/layers.idx
.
它提供了层的列表以及应包含在其中的罐子部分。
层按照应添加到 Docker/OCI 映像的顺序编写。
图层名称写为带引号的字符串,前缀为虚线空格 () 和冒号 ("-·"
":"
) 后缀。
图层内容是文件或目录名称,以空格短划线 () 为前缀的带引号的字符串。
目录名称以 结尾,文件名则不然。
当使用目录名称时,这意味着该目录中的所有文件都在同一层中。"··-·"
/
图层索引的一个典型示例是:
- "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 的物理文件偏移量,如以下示例所示:
myapp.jar +-------------------+-------------------------+ | /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar | |+-----------------+||+-----------+----------+| || A.class ||| B.class | C.class || |+-----------------+||+-----------+----------+| +-------------------+-------------------------+ ^ ^ ^ 0063 3452 3980
前面的示例演示了如何A.class
可以在/BOOT-INF/classes
在myapp.jar
在位置0063
.B.class
从嵌套的 jar 实际上可以在myapp.jar
在位置3452
和C.class
在位置3980
.
有了这些信息,我们就可以通过查找外部 jar 的适当部分来加载特定的嵌套条目。 我们不需要解压存档,也不需要将所有条目数据读入内存。
3. 启动可执行罐
这org.springframework.boot.loader.Launcher
class 是一个特殊的引导类,用作可执行 jar 的主要入口点。
这是实际的Main-Class
,它用于设置适当的URLClassLoader
并最终调用您的main()
方法。
有三个Starters子类(JarLauncher
,WarLauncher
和PropertiesLauncher
).
它们的目的是加载资源 (.class
文件等)来自目录中的嵌套 jar 文件或 war 文件(而不是类路径上显式的那些文件)。
在以下情况下JarLauncher
和WarLauncher
,嵌套路径是固定的。JarLauncher
看BOOT-INF/lib/
和WarLauncher
看WEB-INF/lib/
和WEB-INF/lib-provided/
.
如果您想要更多,您可以在这些位置添加额外的罐子。
这PropertiesLauncher
看BOOT-INF/lib/
默认情况下,在您的应用程序存档中。
您可以通过设置名为LOADER_PATH
或loader.path
在loader.properties
(这是目录、档案或档案中的目录的逗号分隔列表)。
3.1. Starters清单
您需要指定适当的Launcher
作为Main-Class
属性META-INF/MANIFEST.MF
.
您要启动的实际类(即包含main
方法)应在Start-Class
属性。
以下示例显示了典型的MANIFEST.MF
对于可执行的 jar 文件:
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: com.mycompany.project.MyApplication
对于战争文件,如下所示:
Main-Class: org.springframework.boot.loader.WarLauncher Start-Class: com.mycompany.project.MyApplication
您无需指定Class-Path 清单文件中的条目。
类路径是从嵌套的 jar 中推断出来的。 |
4. PropertiesLauncher 功能
PropertiesLauncher
具有一些可以使用外部属性(系统属性、环境变量、清单条目或loader.properties
).
下表描述了这些属性:
钥匙 | 目的 |
---|---|
|
逗号分隔的类路径,例如 |
|
用于解析 |
|
main 方法的默认参数(空格分隔)。 |
|
要启动的主类的名称(例如 |
|
属性文件的名称(例如 |
|
属性文件的路径(例如 |
|
布尔标志,指示应将所有属性添加到系统属性。
它默认为 |
当指定为环境变量或清单条目时,应使用以下名称:
钥匙 | 清单条目 | 环境变量 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
构建插件会自动移动Main-Class 属性设置为Start-Class 当胖罐子建成时。
如果使用它,请使用Main-Class 属性并省略Start-Class . |
以下规则适用于使用PropertiesLauncher
:
-
loader.properties
在loader.home
,然后在类路径的根目录中,然后在classpath:/BOOT-INF/classes
. 使用存在具有该名称的文件的第一个位置。 -
loader.home
是附加属性文件的目录位置(覆盖默认值),仅当loader.config.location
未指定。 -
loader.path
可以包含目录(以递归方式扫描 jar 和 zip 文件)、归档路径、存档中扫描 jar 文件的目录(例如,dependencies.jar!/lib
)或通配符模式(对于默认的 JVM 行为)。 存档路径可以相对于loader.home
或文件系统中的任何位置,使用jar:file:
前缀。 -
loader.path
(如果为空)默认为BOOT-INF/lib
(如果从存档运行,则表示本地目录或嵌套目录)。 正因为如此,PropertiesLauncher
行为与JarLauncher
当未提供其他配置时。 -
loader.path
不能用于配置loader.properties
(用于搜索后者的类路径是 JVM 类路径,当PropertiesLauncher
已启动)。 -
占位符替换是从系统和环境变量以及使用前所有值的属性文件本身完成的。
-
属性的搜索顺序(在多个位置查找是有意义的)是环境变量、系统属性、
loader.properties
、分解的存档清单和存档清单。
5. 可执行 Jar 限制
使用 Spring Boot Loader 打包应用程序时,您需要考虑以下限制:
-
Zip 条目压缩: 这
ZipEntry
对于嵌套的 jar 必须使用ZipEntry.STORED
方法。 这是必需的,以便我们可以直接查找嵌套 jar 中的单个内容。 嵌套 jar 文件本身的内容仍然可以压缩,外部 jar 中的任何其他条目也可以压缩。
-
系统类加载器: 启动的应用程序应使用
Thread.getContextClassLoader()
加载类时(大多数库和框架默认这样做)。 尝试加载嵌套的 jar 类ClassLoader.getSystemClassLoader()
失败。java.util.Logging
始终使用系统类加载器。 因此,您应该考虑使用不同的日志记录实现。