On this page
附录 E.可执行 jar 格式
spring-boot-loader
模块使 Spring Boot 支持可执行的 jar 和 war 文件。如果您使用 Maven 插件或 Gradle 插件,则会自动生成可执行 jar,通常不需要了解其工作方式的详细信息。
如果您需要从其他构建系统创建可执行 jar,或者您只是对基础技术感到好奇,本节将提供一些背景知识。
E.1 嵌套 JAR
Java 没有提供任何标准的方式来加载嵌套的 jar 文件(即,它们本身包含在 jar 中的 jar 文件)。如果您需要分发一个自包含的应用程序,而该应用程序可以从命令行运行而无需解压缩,则可能会出现问题。
为了解决这个问题,许多开发人员使用“shade”Jar子。一个有 shade 的 jar 将所有 jar 中的所有类打包到一个“超级 jar”中。带 shade 的 jar 的问题在于,很难查看应用程序中实际包含哪些库。如果在多个 jar 中使用了相同的文件名(但内容不同),也可能会产生问题。 Spring Boot 采用了另一种方法,实际上允许您直接嵌套 jar。
E.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
目录中。
E.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
中。
E.2 Spring Boot 的“ JarFile”类
用于支持加载嵌套 jar 的核心类是org.springframework.boot.loader.jar.JarFile
。它使您可以从标准 jar 文件或嵌套的子 jar 数据加载 jar 内容。首次加载时,每个JarEntry
的位置都 Map 到外部 jar 的物理文件偏移,如以下示例所示:
myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
|| A.class ||| B.class | C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
^ ^ ^
0063 3452 3980
前面的示例显示了如何在myapp.jar
的0063
的/BOOT-INF/classes
中找到A.class
。嵌套 jar 中的B.class
实际上可以在myapp.jar
的3452
位置上找到,而C.class
在3980
的位置上。
有了这些信息,我们可以通过查找外部 jar 的适当部分来加载特定的嵌套条目。我们不需要解压缩 Files,也不需要将所有条目数据读入内存。
E.2.1 与标准 Java“ JarFile”的兼容性
Spring Boot Loader 努力保持与现有代码和库的兼容性。 org.springframework.boot.loader.jar.JarFile
是java.util.jar.JarFile
的扩展,应作为替代产品。 getURL()
方法返回一个URL
,该URL
打开与java.net.JarURLConnection
兼容的连接,并且可以与 Java 的URLClassLoader
一起使用。
E.3 启动可执行 jar
org.springframework.boot.loader.Launcher
类是特殊的引导程序类,用作可执行 jar 的主要入口点。它是 jar 文件中的实际Main-Class
,用于设置适当的URLClassLoader
并最终调用main()
方法。
存在三个启动器子类(JarLauncher
,WarLauncher
和PropertiesLauncher
)。它们的目的是从目录中的嵌套 jar 文件或 war 文件(而不是在 Classpath 中显式的文件)中加载资源(.class
文件等)。在JarLauncher
和WarLauncher
的情况下,嵌套路径是固定的。 JarLauncher
出现在BOOT-INF/lib/
中,而WarLauncher
出现在WEB-INF/lib/
和WEB-INF/lib-provided/
中。如果需要,可以在这些位置添加额外的Jar子。默认情况下,PropertiesLauncher
在应用程序归档文件中的BOOT-INF/lib/
中查找,但是您可以通过在loader.properties
中设置名为LOADER_PATH
或loader.path
的环境变量(目录,归档文件或归档文件中的目录以逗号分隔)来添加其他位置。
E.3.1 启动 Lists
您需要指定一个适当的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
Note
您无需在 Lists 文件中指定Class-Path
个条目。Classpath 是从嵌套的 jar 中推导出来的。
E.3.2 Exploded archives
某些 PaaS 实施可能选择在运行之前解压缩存档。例如,Cloud Foundry 以这种方式运行。您可以通过启动适当的启动器来运行解压缩的存档,如下所示:
$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher
E.4 属性启动器功能
PropertiesLauncher
具有一些可以通过外部属性(系统属性,环境变量,Lists 条目或loader.properties
)启用的特殊功能。下表描述了这些属性:
Key | Purpose |
---|---|
loader.path |
以逗号分隔的 Classpath,例如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.args |
main 方法的默认参数(以空格分隔)。 |
loader.main |
要启动的主类的名称(例如com.app.Application )。 |
loader.config.name |
属性文件的名称(例如launcher ),默认为loader 。 |
loader.config.location |
属性文件的路径(例如classpath:loader.properties )。默认为loader.properties 。 |
loader.system |
布尔值标志,指示应将所有属性添加到系统属性。默认为false 。 |
当指定为环境变量或 Lists 条目时,应使用以下名称:
Key | Manifest entry | Environment variable |
---|---|---|
loader.path |
Loader-Path |
LOADER_PATH |
loader.home |
Loader-Home |
LOADER_HOME |
loader.args |
Loader-Args |
LOADER_ARGS |
loader.main |
Start-Class |
LOADER_MAIN |
loader.config.location |
Loader-Config-Location |
LOADER_CONFIG_LOCATION |
loader.system |
Loader-System |
LOADER_SYSTEM |
Tip
构建胖Jar时,构建插件会自动将Main-Class
属性移动到Start-Class
。如果使用它,请使用Main-Class
属性并省略Start-Class
来指定要启动的类的名称。
以下规则适用于使用PropertiesLauncher
:
在
loader.home
中搜索loader.properties
,然后在 Classpath 的根目录中搜索,然后在classpath:/BOOT-INF/classes
中搜索。使用具有该名称的文件的第一个位置。仅当未指定
loader.config.location
时,loader.home
是其他属性文件的目录位置(覆盖默认值)。loader.path
可以包含目录(以递归方式扫描 jar 和 zip 文件),存档路径,存档中的一个目录以扫描 jar 文件(例如dependencies.jar!/lib
)或通配符模式(用于默认的 JVM 行为)。存档路径可以相对于loader.home
或文件系统中带有jar:file:
前缀的任何位置。loader.path
(如果为空)默认为BOOT-INF/lib
(表示本地目录,如果是从存档运行则表示嵌套目录)。因此,当未提供其他配置时,PropertiesLauncher
的行为与JarLauncher
相同。loader.path
不能用于配置loader.properties
的位置(用于搜索后者的 Classpath 是启动PropertiesLauncher
时的 JVM Classpath)。占位符的替换是使用系统和环境变量以及属性文件本身的所有值进行的,然后再使用。
属性(在多个位置中有意义的查找)的搜索 Sequences 是环境变量,系统属性
loader.properties
,分解的 FilesLists 和 FilesLists。
E.5 可执行 Jar 限制
使用 Spring Boot Loader 打包的应用程序时,需要考虑以下限制:
- Zip 压缩:嵌套 jar 的
ZipEntry
必须使用ZipEntry.STORED
方法保存。这是必需的,以便我们可以直接在嵌套 jar 中查找单个内容。嵌套 jar 文件本身的内容仍然可以压缩,外部 jar 中的任何其他条目也可以压缩。
- 系统 classLoader:启动的应用程序在加载类时应使用
Thread.getContextClassLoader()
(默认情况下,大多数库和框架都使用Thread.getContextClassLoader()
)。尝试使用ClassLoader.getSystemClassLoader()
加载嵌套 jar 类失败。java.util.Logging
始终使用系统类加载器。因此,您应该考虑使用其他日志记录实现。
E.6 替代独立 Jar 包解决方案
如果上述限制意味着您不能使用 Spring Boot Loader,请考虑以下替代方法: