这些spring-boot-loader
模块让 Spring Boot 支持可执行的 jar 和 war 文件。如果您使用 Maven 插件或 Gradle 插件,则会自动生成可执行 jar,您通常不需要了解它们如何工作的详细信息。
如果您需要从不同的构建系统创建可执行 jar,或者您只是对底层技术感到好奇,本附录提供了一些背景知识。
1. 嵌套 JAR
Java 不提供任何标准方法来加载嵌套 jar 文件(即,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. 可执行的 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/
目录下包含额外的索引文件。可以为 jar 和 war 提供一个classpath.idx
文件,并且它提供了 jar 应添加到类路径中的顺序。该layers.idx
文件只能用于 jar,并且它允许将 jar 拆分为逻辑层以创建 Docker/OCI 映像。
索引文件遵循 YAML 兼容语法,以便第三方工具可以轻松解析它们。但是,这些文件不会在内部解析为 YAML,它们必须完全按照下面描述的格式编写才能使用。
1.4. 类路径索引
类路径索引文件可以在BOOT-INF/classpath.idx
. 它按照应添加到类路径的顺序提供了 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
. 它提供了层列表以及应包含在其中的 jar 部分。层按照应添加到 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
前面的示例显示了如何在in位置A.class
中找到。
从嵌套的 jar 中实际上可以在at 位置 处找到,并且is at 位置。/BOOT-INF/classes
myapp.jar
0063
B.class
myapp.jar
3452
C.class
3980
有了这些信息,我们就可以通过寻找外部 jar 的适当部分来加载特定的嵌套条目。我们不需要解压存档,也不需要将所有条目数据读入内存。
3. 启动可执行Jar
该类org.springframework.boot.loader.Launcher
是一个特殊的引导类,用作可执行 jar 的主要入口点。它是您的 jar 文件中的实际内容Main-Class
,用于设置适当的URLClassLoader
并最终调用您的main()
方法。
共有三个启动器子类(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
in的环境变量loader.properties
(它是目录、档案或档案中的目录的逗号分隔列表)来添加其他位置。
3.1. 启动器清单
您需要指定一个适当的属性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
对于 war 文件,它将如下所示:
Main-Class: org.springframework.boot.loader.WarLauncher Start-Class: com.mycompany.project.MyApplication
您无需Class-Path 在清单文件中指定条目。类路径是从嵌套的 jar 中推导出来的。
|
4.PropertiesLauncher功能
PropertiesLauncher
有一些可以通过外部属性(系统属性、环境变量、清单条目或loader.properties
)启用的特殊功能。下表描述了这些属性:
钥匙 | 目的 |
---|---|
|
以逗号分隔的类路径,例如 |
|
用于解析 |
|
主方法的默认参数(空格分隔)。 |
|
要启动的主类的名称(例如, |
|
属性文件的名称(例如, |
|
属性文件的路径(例如, |
|
布尔标志,指示所有属性都应添加到系统属性中。它默认为 |
当指定为环境变量或清单条目时,应使用以下名称:
钥匙 | 舱单条目 | 环境变量 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
构建插件会自动将Main-Class 属性移动到Start-Class 构建 fat jar 时。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
(用于搜索后者的类路径是PropertiesLauncher
启动时的JVM类路径)。 -
占位符替换是在使用前从系统和环境变量以及属性文件本身对所有值完成的。
-
属性的搜索顺序(在多个位置查找是有意义的)是环境变量、系统属性、
loader.properties
展开的存档清单和存档清单。
5. 可执行Jar限制
使用 Spring Boot Loader 打包应用程序时,您需要考虑以下限制:
-
Zip 条目压缩:
ZipEntry
必须使用该方法保存嵌套 jar 的 Zip 条目压缩ZipEntry.STORED
。这是必需的,以便我们可以直接查找嵌套 jar 中的各个内容。嵌套 jar 文件本身的内容仍然可以被压缩,外部 jar 中的任何其他条目也可以。
-
系统类加载器:启动的应用程序应在加载类时使用
Thread.getContextClassLoader()
(大多数库和框架默认这样做)。尝试加载嵌套 jar 类ClassLoader.getSystemClassLoader()
失败。java.util.Logging
始终使用系统类加载器。因此,您应该考虑不同的日志记录实现。